Allow optional sources in derive

This commit is contained in:
Gavrilikhin Daniil 2023-10-05 23:47:58 +08:00
parent ba313282a8
commit 0d68f798a5
2 changed files with 81 additions and 3 deletions

View File

@ -10,6 +10,7 @@ use crate::{
pub struct SourceCode {
source_code: syn::Member,
is_option: bool,
}
impl SourceCode {
@ -27,6 +28,19 @@ impl SourceCode {
for (i, field) in fields.iter().enumerate() {
for attr in &field.attrs {
if attr.path().is_ident("source_code") {
let is_option = if let syn::Type::Path(syn::TypePath {
path: syn::Path { segments, .. },
..
}) = &field.ty
{
segments
.last()
.map(|seg| seg.ident == "Option")
.unwrap_or(false)
} else {
false
};
let source_code = if let Some(ident) = field.ident.clone() {
syn::Member::Named(ident)
} else {
@ -35,7 +49,10 @@ impl SourceCode {
span: field.span(),
})
};
return Ok(Some(SourceCode { source_code }));
return Ok(Some(SourceCode {
source_code,
is_option,
}));
}
}
}
@ -45,11 +62,21 @@ impl SourceCode {
pub(crate) fn gen_struct(&self, fields: &syn::Fields) -> Option<TokenStream> {
let (display_pat, _display_members) = display_pat_members(fields);
let src = &self.source_code;
let ret = if self.is_option {
quote! {
self.#src.as_ref().map(|s| s as _)
}
} else {
quote! {
Some(&self.#src)
}
};
Some(quote! {
#[allow(unused_variables)]
fn source_code(&self) -> std::option::Option<&dyn miette::SourceCode> {
let Self #display_pat = self;
Some(&self.#src)
#ret
}
})
}
@ -68,10 +95,19 @@ impl SourceCode {
}
};
let variant_name = ident.clone();
let ret = if source_code.is_option {
quote! {
#field.as_ref().map(|s| s as _)
}
} else {
quote! {
std::option::Option::Some(#field)
}
};
match &fields {
syn::Fields::Unit => None,
_ => Some(quote! {
Self::#variant_name #display_pat => std::option::Option::Some(#field),
Self::#variant_name #display_pat => #ret,
}),
}
})

View File

@ -584,3 +584,45 @@ fn test_unit_enum_display() {
"hello from unit help"
);
}
#[test]
fn test_optional_source_code() {
#[derive(Debug, Diagnostic, Error)]
#[error("struct with optional source")]
struct Struct {
#[source_code]
src: Option<String>,
}
assert!(Struct { src: None }.source_code().is_none());
assert!(Struct {
src: Some("".to_string())
}
.source_code()
.is_some());
#[derive(Debug, Diagnostic, Error)]
enum Enum {
#[error("variant1 with optional source")]
Variant1 {
#[source_code]
src: Option<String>,
},
#[error("variant2 with optional source")]
Variant2 {
#[source_code]
src: Option<String>,
},
}
assert!(Enum::Variant1 { src: None }.source_code().is_none());
assert!(Enum::Variant1 {
src: Some("".to_string())
}
.source_code()
.is_some());
assert!(Enum::Variant2 { src: None }.source_code().is_none());
assert!(Enum::Variant2 {
src: Some("".to_string())
}
.source_code()
.is_some());
}