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}