nix_compat_derive/
ser.rs

1use proc_macro2::{Span, TokenStream};
2use quote::{quote, quote_spanned};
3use syn::spanned::Spanned;
4use syn::{DeriveInput, Generics, Path, Type};
5
6use crate::internal::attrs::Default;
7use crate::internal::inputs::RemoteInput;
8use crate::internal::{Container, Context, Data, Field, Remote, Style, Variant, attrs};
9
10pub fn expand_nix_serialize(crate_path: Path, input: &mut DeriveInput) -> syn::Result<TokenStream> {
11    let cx = Context::new();
12    let cont = Container::from_ast(&cx, crate_path, input);
13    cx.check()?;
14    let cont = cont.unwrap();
15
16    let ty = cont.ident_type();
17    let body = nix_serialize_body(&cont);
18    let crate_path = cont.crate_path();
19
20    Ok(nix_serialize_impl(
21        crate_path,
22        &ty,
23        &cont.original.generics,
24        body,
25    ))
26}
27
28pub fn expand_nix_serialize_remote(
29    crate_path: Path,
30    input: &RemoteInput,
31) -> syn::Result<TokenStream> {
32    let cx = Context::new();
33    let remote = Remote::from_ast(&cx, crate_path, input);
34    if let Some(attrs) = remote.as_ref().map(|r| &r.attrs)
35        && attrs.display.is_none()
36        && attrs.type_into.is_none()
37        && attrs.type_try_into.is_none()
38    {
39        cx.error_spanned(input, "Missing into, try_into or display attribute");
40    }
41    cx.check()?;
42    let remote = remote.unwrap();
43
44    let crate_path = remote.crate_path();
45    let body = nix_serialize_body_into(crate_path, &remote.attrs).expect("From tokenstream");
46    let generics = Generics::default();
47    Ok(nix_serialize_impl(crate_path, remote.ty, &generics, body))
48}
49
50fn nix_serialize_impl(
51    crate_path: &Path,
52    ty: &Type,
53    generics: &Generics,
54    body: TokenStream,
55) -> TokenStream {
56    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
57
58    quote! {
59        #[automatically_derived]
60        impl #impl_generics #crate_path::wire::ser::NixSerialize for #ty #ty_generics
61            #where_clause
62        {
63            async fn serialize<W>(&self, writer: &mut W) -> std::result::Result<(), W::Error>
64                where W: #crate_path::wire::ser::NixWrite
65            {
66                use #crate_path::wire::ser::Error as _;
67                #body
68            }
69        }
70    }
71}
72
73fn nix_serialize_body_into(
74    crate_path: &syn::Path,
75    attrs: &attrs::Container,
76) -> Option<TokenStream> {
77    if let Default::Default(span) = &attrs.display {
78        Some(nix_serialize_display(span.span()))
79    } else if let Default::Path(path) = &attrs.display {
80        Some(nix_serialize_display_path(path))
81    } else if let Some(type_into) = attrs.type_into.as_ref() {
82        Some(nix_serialize_into(type_into))
83    } else {
84        attrs
85            .type_try_into
86            .as_ref()
87            .map(|type_try_into| nix_serialize_try_into(crate_path, type_try_into))
88    }
89}
90
91fn nix_serialize_body(cont: &Container) -> TokenStream {
92    if let Some(tokens) = nix_serialize_body_into(cont.crate_path(), &cont.attrs) {
93        tokens
94    } else {
95        match &cont.data {
96            Data::Struct(_style, fields) => nix_serialize_struct(fields),
97            Data::Enum(variants) => nix_serialize_enum(variants),
98        }
99    }
100}
101
102fn nix_serialize_struct(fields: &[Field<'_>]) -> TokenStream {
103    let write_fields = fields.iter().map(|f| {
104        let field = &f.member;
105        let ty = f.ty;
106        let write_value = quote_spanned! {
107            ty.span()=> writer.write_value(&self.#field).await?
108        };
109        if let Some(version) = f.attrs.version.as_ref() {
110            quote! {
111                if (#version).contains(&writer.version().minor()) {
112                    #write_value;
113                }
114            }
115        } else {
116            quote! {
117                #write_value;
118            }
119        }
120    });
121
122    quote! {
123        #(#write_fields)*
124        Ok(())
125    }
126}
127
128fn nix_serialize_variant(variant: &Variant<'_>) -> TokenStream {
129    let ident = variant.ident;
130    let write_fields = variant.fields.iter().map(|f| {
131        let field = f.var_ident();
132        let ty = f.ty;
133        let write_value = quote_spanned! {
134            ty.span()=> writer.write_value(#field).await?
135        };
136        if let Some(version) = f.attrs.version.as_ref() {
137            quote! {
138                if (#version).contains(&writer.version().minor()) {
139                    #write_value;
140                }
141            }
142        } else {
143            quote! {
144                #write_value;
145            }
146        }
147    });
148    let field_names = variant.fields.iter().map(|f| f.var_ident());
149    let destructure = match variant.style {
150        Style::Struct => {
151            quote! {
152                Self::#ident { #(#field_names),* }
153            }
154        }
155        Style::Tuple => {
156            quote! {
157                Self::#ident(#(#field_names),*)
158            }
159        }
160        Style::Unit => quote!(Self::#ident),
161    };
162    let ignore = match variant.style {
163        Style::Struct => {
164            quote! {
165                Self::#ident { .. }
166            }
167        }
168        Style::Tuple => {
169            quote! {
170                Self::#ident(_, ..)
171            }
172        }
173        Style::Unit => quote!(Self::#ident),
174    };
175    let version = &variant.attrs.version;
176    quote! {
177        #destructure if (#version).contains(&writer.version().minor()) => {
178            #(#write_fields)*
179        }
180        #ignore => {
181            return Err(W::Error::invalid_enum(format!("{} is not valid for version {}", stringify!(#ident), writer.version())));
182        }
183    }
184}
185
186fn nix_serialize_enum(variants: &[Variant<'_>]) -> TokenStream {
187    let match_variant = variants
188        .iter()
189        .map(|variant| nix_serialize_variant(variant));
190    quote! {
191        match self {
192            #(#match_variant)*
193        }
194        Ok(())
195    }
196}
197
198fn nix_serialize_into(ty: &Type) -> TokenStream {
199    quote_spanned! {
200        ty.span() =>
201        {
202            let other : #ty = <Self as Clone>::clone(self).into();
203            writer.write_value(&other).await
204        }
205    }
206}
207
208fn nix_serialize_try_into(crate_path: &Path, ty: &Type) -> TokenStream {
209    quote_spanned! {
210        ty.span() =>
211        {
212            use #crate_path::wire::ser::Error;
213            let other : #ty = <Self as Clone>::clone(self).try_into().map_err(Error::unsupported_data)?;
214            writer.write_value(&other).await
215        }
216    }
217}
218
219fn nix_serialize_display(span: Span) -> TokenStream {
220    quote_spanned! {
221        span => writer.write_display(self).await
222    }
223}
224
225fn nix_serialize_display_path(path: &syn::ExprPath) -> TokenStream {
226    quote_spanned! {
227        path.span() => writer.write_display(#path(self)).await
228    }
229}