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