marine_macro_impl/parse_macro_input/
item_record.rs1use super::ParseMacroInput;
18use crate::ast_types;
19use crate::ast_types::AstRecordField;
20use crate::ast_types::AstRecordFields;
21use crate::ast_types::MarineAst;
22use crate::syn_error;
23use crate::parsed_type::ParsedType;
24
25use syn::Result;
26use syn::spanned::Spanned;
27
28impl ParseMacroInput for syn::ItemStruct {
29 fn parse_macro_input(self) -> Result<MarineAst> {
30 check_record(&self)?;
31
32 let fields = match &self.fields {
33 syn::Fields::Named(named_fields) => &named_fields.named,
34 _ => return syn_error!(self.span(), "only named fields are allowed in structs"),
35 };
36
37 let fields = fields_into_ast(fields, &self.ident)?;
38 let fields = AstRecordFields::Named(fields);
39
40 let name = self.ident.to_string();
41 let ast_record_item = ast_types::AstRecord {
42 name,
43 fields,
44 original: self,
45 };
46 let ast_record_item = MarineAst::Record(Box::new(ast_record_item));
47
48 Ok(ast_record_item)
49 }
50}
51
52fn check_record(record: &syn::ItemStruct) -> Result<()> {
53 if record.generics.lt_token.is_some()
54 || record.generics.gt_token.is_some()
55 || record.generics.where_clause.is_some()
56 {
57 return syn_error!(
58 record.span(),
59 "#[marine] couldn't be applied to a struct with generics or lifetimes"
60 );
61 }
62
63 Ok(())
64}
65
66fn fields_into_ast(
67 fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
68 record_ident: &syn::Ident,
69) -> Result<Vec<AstRecordField>> {
70 fields
71 .iter()
72 .map(|field| {
73 maybe_warn_about_non_doc_attributes(field, record_ident);
74
75 let name = field.ident.as_ref().map(|ident| {
76 ident
77 .to_string()
78 .split(' ')
79 .last()
80 .unwrap_or_default()
81 .to_string()
82 });
83 let ty = ParsedType::from_type(&field.ty)?;
84
85 let record_field = AstRecordField { name, ty };
86 Ok(record_field)
87 })
88 .collect::<Result<Vec<_>>>()
89}
90
91fn maybe_warn_about_non_doc_attributes(field: &syn::Field, record_ident: &syn::Ident) {
93 for attr in field.attrs.iter() {
94 match attr.parse_meta() {
95 Ok(meta) if is_doc_attribute(&meta) => continue,
96 _ => {}
97 }
98
99 match &field.ident {
102 Some(ident) => eprintln!(
103 r#"warning: field "{}" of struct "{}" has an attribute which could cause compatibility issues"#,
104 ident, record_ident
105 ),
106 None => eprintln!(
107 r#"warning: field of struct "{}" has an attribute which could cause compatibility issues"#,
108 record_ident
109 ),
110 };
111 }
112}
113
114fn is_doc_attribute(meta: &syn::Meta) -> bool {
115 const DOC_ATTR_NAME: &str = "doc";
116
117 meta.path().is_ident(DOC_ATTR_NAME)
118}