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}