Skip to main content

spacetime_bindings_macro_input/
sats.rs

1extern crate core;
2extern crate proc_macro;
3
4use proc_macro2::TokenStream;
5use quote::ToTokens;
6use syn::LitStr;
7
8use super::sym;
9use super::util::{check_duplicate, match_meta};
10
11pub struct SatsType<'a> {
12    pub ident: &'a syn::Ident,
13    pub generics: &'a syn::Generics,
14    pub name: LitStr,
15    pub krate: TokenStream,
16    // may want to use in the future
17    #[allow(unused)]
18    pub original_attrs: &'a [syn::Attribute],
19    pub data: SatsTypeData<'a>,
20    /// Was the type marked as `#[repr(C)]`?
21    pub is_repr_c: bool,
22}
23
24pub enum SatsTypeData<'a> {
25    Product(Vec<SatsField<'a>>),
26    Sum(Vec<SatsVariant<'a>>),
27}
28
29#[derive(Clone)]
30pub struct SatsField<'a> {
31    pub ident: Option<&'a syn::Ident>,
32    pub vis: &'a syn::Visibility,
33    pub name: Option<String>,
34    pub ty: &'a syn::Type,
35    pub original_attrs: &'a [syn::Attribute],
36}
37
38pub struct SatsVariant<'a> {
39    pub ident: &'a syn::Ident,
40    pub name: String,
41    pub ty: Option<&'a syn::Type>,
42    pub member: Option<syn::Member>,
43    // may want to use in the future
44    #[allow(unused)]
45    pub original_attrs: &'a [syn::Attribute],
46}
47
48pub fn sats_type_from_derive(
49    input: &syn::DeriveInput,
50    crate_fallback: TokenStream,
51) -> syn::Result<SatsType<'_>> {
52    let data = match &input.data {
53        syn::Data::Struct(struc) => {
54            let fields = struc.fields.iter().map(|field| SatsField {
55                ident: field.ident.as_ref(),
56                vis: &field.vis,
57                name: field.ident.as_ref().map(syn::Ident::to_string),
58                ty: &field.ty,
59                original_attrs: &field.attrs,
60            });
61            SatsTypeData::Product(fields.collect())
62        }
63        syn::Data::Enum(enu) => {
64            let variants = enu.variants.iter().map(|var| {
65                let (member, ty) = variant_data(var)?.unzip();
66                Ok(SatsVariant {
67                    ident: &var.ident,
68                    name: var.ident.to_string(),
69                    ty,
70                    member,
71                    original_attrs: &var.attrs,
72                })
73            });
74            SatsTypeData::Sum(variants.collect::<syn::Result<Vec<_>>>()?)
75        }
76        syn::Data::Union(u) => {
77            return Err(syn::Error::new(u.union_token.span, "unions not supported"));
78        }
79    };
80    extract_sats_type(
81        &input.ident,
82        &input.generics,
83        &input.attrs,
84        data,
85        crate_fallback,
86    )
87}
88
89fn is_repr_c(attrs: &[syn::Attribute]) -> bool {
90    let mut is_repr_c = false;
91    for attr in attrs.iter().filter(|a| a.path() == sym::repr) {
92        let _ = attr.parse_nested_meta(|meta| {
93            is_repr_c |= meta.path.is_ident("C");
94            Ok(())
95        });
96    }
97    is_repr_c
98}
99
100fn extract_sats_type<'a>(
101    ident: &'a syn::Ident,
102    generics: &'a syn::Generics,
103    attrs: &'a [syn::Attribute],
104    data: SatsTypeData<'a>,
105    crate_fallback: TokenStream,
106) -> syn::Result<SatsType<'a>> {
107    let mut name = None;
108    let mut krate = None;
109    for attr in attrs {
110        if attr.path() != sym::sats {
111            continue;
112        }
113        attr.parse_nested_meta(|meta| {
114            match_meta!(match meta {
115                sym::crate_ => {
116                    check_duplicate(&krate, &meta)?;
117                    let value = meta.value()?;
118                    let v = value.call(syn::Path::parse_mod_style)?;
119                    krate = Some(v.into_token_stream());
120                }
121                sym::name => {
122                    check_duplicate(&name, &meta)?;
123                    let value = meta.value()?;
124                    let v = value.parse::<LitStr>()?;
125                    name = Some(v);
126                }
127            });
128            Ok(())
129        })?;
130    }
131    let krate = krate.unwrap_or(crate_fallback);
132    let name = name.unwrap_or_else(|| crate::util::ident_to_litstr(ident));
133
134    let is_repr_c = is_repr_c(attrs);
135
136    Ok(SatsType {
137        ident,
138        generics,
139        name,
140        krate,
141        original_attrs: attrs,
142        data,
143        is_repr_c,
144    })
145}
146
147fn variant_data(variant: &syn::Variant) -> syn::Result<Option<(syn::Member, &syn::Type)>> {
148    let field = match &variant.fields {
149        syn::Fields::Named(f) if f.named.len() == 1 => &f.named[0],
150        syn::Fields::Named(_) => {
151            return Err(syn::Error::new_spanned(
152                &variant.fields,
153                "must be a unit variant or a newtype variant",
154            ));
155        }
156        syn::Fields::Unnamed(f) if f.unnamed.len() != 1 => {
157            return Err(syn::Error::new_spanned(
158                &variant.fields,
159                "must be a unit variant or a newtype variant",
160            ));
161        }
162        syn::Fields::Unnamed(f) => &f.unnamed[0],
163        syn::Fields::Unit => return Ok(None),
164    };
165    let member = field
166        .ident
167        .clone()
168        .map(Into::into)
169        .unwrap_or_else(|| syn::Member::from(0));
170    Ok(Some((member, &field.ty)))
171}