1use quote::ToTokens;
2use syn::meta::ParseNestedMeta;
3use syn::parse::Parse;
4use syn::{Attribute, Expr, ExprLit, ExprPath, Lit, Token, parse_quote};
5
6use super::Context;
7use super::symbol::{
8 CRATE, DEFAULT, DISPLAY, FROM, FROM_STR, INTO, NIX, Symbol, TRY_FROM, TRY_INTO, VERSION,
9};
10
11#[derive(Debug, PartialEq, Eq)]
12pub enum Default {
13 None,
14 #[allow(clippy::enum_variant_names)]
15 Default(syn::Path),
16 Path(ExprPath),
17}
18
19impl Default {
20 pub fn is_none(&self) -> bool {
21 matches!(self, Default::None)
22 }
23}
24
25#[derive(Debug, PartialEq, Eq)]
26pub struct Field {
27 pub default: Default,
28 pub version: Option<syn::ExprRange>,
29}
30
31impl Field {
32 pub fn from_ast(ctx: &Context, attrs: &Vec<Attribute>) -> Field {
33 let mut version = None;
34 let mut version_path = None;
35 let mut default = Default::None;
36 for attr in attrs {
37 if attr.path() != NIX {
38 continue;
39 }
40 if let Err(err) = attr.parse_nested_meta(|meta| {
41 if meta.path == VERSION {
42 version = parse_lit(ctx, &meta, VERSION)?;
43 version_path = Some(meta.path);
44 } else if meta.path == DEFAULT {
45 if meta.input.peek(Token![=]) {
46 if let Some(path) = parse_lit(ctx, &meta, DEFAULT)? {
47 default = Default::Path(path);
48 }
49 } else {
50 default = Default::Default(meta.path);
51 }
52 } else {
53 let path = meta.path.to_token_stream().to_string();
54 return Err(meta.error(format_args!("unknown nix field attribute '{path}'")));
55 }
56 Ok(())
57 }) {
58 eprintln!("{:?}", err.span().source_text());
59 ctx.syn_error(err);
60 }
61 }
62 if version.is_some() && default.is_none() {
63 default = Default::Default(version_path.unwrap());
64 }
65
66 Field { default, version }
67 }
68}
69
70#[derive(Debug, PartialEq, Eq)]
71pub struct Variant {
72 pub version: syn::ExprRange,
73}
74
75impl Variant {
76 pub fn from_ast(ctx: &Context, attrs: &Vec<Attribute>) -> Variant {
77 let mut version = parse_quote!(..);
78 for attr in attrs {
79 if attr.path() != NIX {
80 continue;
81 }
82 if let Err(err) = attr.parse_nested_meta(|meta| {
83 if meta.path == VERSION {
84 if let Some(v) = parse_lit(ctx, &meta, VERSION)? {
85 version = v;
86 }
87 } else {
88 let path = meta.path.to_token_stream().to_string();
89 return Err(meta.error(format_args!("unknown nix variant attribute '{path}'")));
90 }
91 Ok(())
92 }) {
93 eprintln!("{:?}", err.span().source_text());
94 ctx.syn_error(err);
95 }
96 }
97
98 Variant { version }
99 }
100}
101
102#[derive(Debug, PartialEq, Eq)]
103pub struct Container {
104 pub from_str: Option<syn::Path>,
105 pub type_from: Option<syn::Type>,
106 pub type_try_from: Option<syn::Type>,
107 pub type_into: Option<syn::Type>,
108 pub type_try_into: Option<syn::Type>,
109 pub display: Default,
110 pub crate_path: Option<syn::Path>,
111}
112
113impl Container {
114 pub fn from_ast(ctx: &Context, attrs: &Vec<Attribute>) -> Container {
115 let mut type_from = None;
116 let mut type_try_from = None;
117 let mut crate_path = None;
118 let mut from_str = None;
119 let mut type_into = None;
120 let mut type_try_into = None;
121 let mut display = Default::None;
122
123 for attr in attrs {
124 if attr.path() != NIX {
125 continue;
126 }
127 if let Err(err) = attr.parse_nested_meta(|meta| {
128 if meta.path == FROM {
129 type_from = parse_lit(ctx, &meta, FROM)?;
130 } else if meta.path == TRY_FROM {
131 type_try_from = parse_lit(ctx, &meta, TRY_FROM)?;
132 } else if meta.path == FROM_STR {
133 from_str = Some(meta.path);
134 } else if meta.path == INTO {
135 type_into = parse_lit(ctx, &meta, INTO)?;
136 } else if meta.path == TRY_INTO {
137 type_try_into = parse_lit(ctx, &meta, TRY_INTO)?;
138 } else if meta.path == DISPLAY {
139 if meta.input.peek(Token![=]) {
140 if let Some(path) = parse_lit(ctx, &meta, DISPLAY)? {
141 display = Default::Path(path);
142 }
143 } else {
144 display = Default::Default(meta.path);
145 }
146 } else if meta.path == CRATE {
147 crate_path = parse_lit(ctx, &meta, CRATE)?;
148 } else {
149 let path = meta.path.to_token_stream().to_string();
150 return Err(meta.error(format_args!("unknown nix variant attribute '{path}'")));
151 }
152 Ok(())
153 }) {
154 eprintln!("{:?}", err.span().source_text());
155 ctx.syn_error(err);
156 }
157 }
158
159 Container {
160 from_str,
161 type_from,
162 type_try_from,
163 type_into,
164 type_try_into,
165 display,
166 crate_path,
167 }
168 }
169}
170
171pub fn get_lit_str(
172 ctx: &Context,
173 meta: &ParseNestedMeta,
174 attr: Symbol,
175) -> syn::Result<Option<syn::LitStr>> {
176 let expr: Expr = meta.value()?.parse()?;
177 let mut value = &expr;
178 while let Expr::Group(e) = value {
179 value = &e.expr;
180 }
181 if let Expr::Lit(ExprLit {
182 lit: Lit::Str(s), ..
183 }) = value
184 {
185 Ok(Some(s.clone()))
186 } else {
187 ctx.error_spanned(
188 expr,
189 format_args!("expected nix attribute {attr} to be string"),
190 );
191 Ok(None)
192 }
193}
194
195pub fn parse_lit<T: Parse>(
196 ctx: &Context,
197 meta: &ParseNestedMeta,
198 attr: Symbol,
199) -> syn::Result<Option<T>> {
200 match get_lit_str(ctx, meta, attr)? {
201 Some(lit) => Ok(Some(lit.parse()?)),
202 None => Ok(None),
203 }
204}
205
206#[cfg(test)]
207mod test {
208 use syn::{Attribute, parse_quote};
209
210 use crate::internal::Context;
211
212 use super::*;
213
214 #[test]
215 fn parse_field_version() {
216 let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(version="..34")])];
217 let ctx = Context::new();
218 let field = Field::from_ast(&ctx, &attrs);
219 ctx.check().unwrap();
220 assert_eq!(
221 field,
222 Field {
223 default: Default::Default(parse_quote!(version)),
224 version: Some(parse_quote!(..34)),
225 }
226 );
227 }
228
229 #[test]
230 fn parse_field_default() {
231 let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(default)])];
232 let ctx = Context::new();
233 let field = Field::from_ast(&ctx, &attrs);
234 ctx.check().unwrap();
235 assert_eq!(
236 field,
237 Field {
238 default: Default::Default(parse_quote!(default)),
239 version: None,
240 }
241 );
242 }
243
244 #[test]
245 fn parse_field_default_path() {
246 let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(default="Default::default")])];
247 let ctx = Context::new();
248 let field = Field::from_ast(&ctx, &attrs);
249 ctx.check().unwrap();
250 assert_eq!(
251 field,
252 Field {
253 default: Default::Path(parse_quote!(Default::default)),
254 version: None,
255 }
256 );
257 }
258
259 #[test]
260 fn parse_field_both() {
261 let attrs: Vec<Attribute> =
262 vec![parse_quote!(#[nix(version="..", default="Default::default")])];
263 let ctx = Context::new();
264 let field = Field::from_ast(&ctx, &attrs);
265 ctx.check().unwrap();
266 assert_eq!(
267 field,
268 Field {
269 default: Default::Path(parse_quote!(Default::default)),
270 version: Some(parse_quote!(..)),
271 }
272 );
273 }
274
275 #[test]
276 fn parse_field_both_rev() {
277 let attrs: Vec<Attribute> =
278 vec![parse_quote!(#[nix(default="Default::default", version="..")])];
279 let ctx = Context::new();
280 let field = Field::from_ast(&ctx, &attrs);
281 ctx.check().unwrap();
282 assert_eq!(
283 field,
284 Field {
285 default: Default::Path(parse_quote!(Default::default)),
286 version: Some(parse_quote!(..)),
287 }
288 );
289 }
290
291 #[test]
292 fn parse_field_no_attr() {
293 let attrs: Vec<Attribute> = vec![];
294 let ctx = Context::new();
295 let field = Field::from_ast(&ctx, &attrs);
296 ctx.check().unwrap();
297 assert_eq!(
298 field,
299 Field {
300 default: Default::None,
301 version: None,
302 }
303 );
304 }
305
306 #[test]
307 fn parse_field_no_subattrs() {
308 let attrs: Vec<Attribute> = vec![parse_quote!(#[nix()])];
309 let ctx = Context::new();
310 let field = Field::from_ast(&ctx, &attrs);
311 ctx.check().unwrap();
312 assert_eq!(
313 field,
314 Field {
315 default: Default::None,
316 version: None,
317 }
318 );
319 }
320
321 #[test]
322 fn parse_variant_version() {
323 let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(version="..34")])];
324 let ctx = Context::new();
325 let variant = Variant::from_ast(&ctx, &attrs);
326 ctx.check().unwrap();
327 assert_eq!(
328 variant,
329 Variant {
330 version: parse_quote!(..34),
331 }
332 );
333 }
334
335 #[test]
336 fn parse_variant_no_attr() {
337 let attrs: Vec<Attribute> = vec![];
338 let ctx = Context::new();
339 let variant = Variant::from_ast(&ctx, &attrs);
340 ctx.check().unwrap();
341 assert_eq!(
342 variant,
343 Variant {
344 version: parse_quote!(..),
345 }
346 );
347 }
348
349 #[test]
350 fn parse_variant_no_subattrs() {
351 let attrs: Vec<Attribute> = vec![parse_quote!(#[nix()])];
352 let ctx = Context::new();
353 let variant = Variant::from_ast(&ctx, &attrs);
354 ctx.check().unwrap();
355 assert_eq!(
356 variant,
357 Variant {
358 version: parse_quote!(..),
359 }
360 );
361 }
362
363 #[test]
364 fn parse_container_from_str() {
365 let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(from_str)])];
366 let ctx = Context::new();
367 let container = Container::from_ast(&ctx, &attrs);
368 ctx.check().unwrap();
369 assert_eq!(
370 container,
371 Container {
372 from_str: Some(parse_quote!(from_str)),
373 type_from: None,
374 type_try_from: None,
375 type_into: None,
376 type_try_into: None,
377 display: Default::None,
378 crate_path: None,
379 }
380 );
381 }
382
383 #[test]
384 fn parse_container_from() {
385 let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(from="u64")])];
386 let ctx = Context::new();
387 let container = Container::from_ast(&ctx, &attrs);
388 ctx.check().unwrap();
389 assert_eq!(
390 container,
391 Container {
392 from_str: None,
393 type_from: Some(parse_quote!(u64)),
394 type_try_from: None,
395 type_into: None,
396 type_try_into: None,
397 display: Default::None,
398 crate_path: None,
399 }
400 );
401 }
402
403 #[test]
404 fn parse_container_try_from() {
405 let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(try_from="u64")])];
406 let ctx = Context::new();
407 let container = Container::from_ast(&ctx, &attrs);
408 ctx.check().unwrap();
409 assert_eq!(
410 container,
411 Container {
412 from_str: None,
413 type_from: None,
414 type_try_from: Some(parse_quote!(u64)),
415 type_into: None,
416 type_try_into: None,
417 display: Default::None,
418 crate_path: None,
419 }
420 );
421 }
422
423 #[test]
424 fn parse_container_into() {
425 let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(into="u64")])];
426 let ctx = Context::new();
427 let container = Container::from_ast(&ctx, &attrs);
428 ctx.check().unwrap();
429 assert_eq!(
430 container,
431 Container {
432 from_str: None,
433 type_from: None,
434 type_try_from: None,
435 type_into: Some(parse_quote!(u64)),
436 type_try_into: None,
437 display: Default::None,
438 crate_path: None,
439 }
440 );
441 }
442
443 #[test]
444 fn parse_container_try_into() {
445 let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(try_into="u64")])];
446 let ctx = Context::new();
447 let container = Container::from_ast(&ctx, &attrs);
448 ctx.check().unwrap();
449 assert_eq!(
450 container,
451 Container {
452 from_str: None,
453 type_from: None,
454 type_try_from: None,
455 type_into: None,
456 type_try_into: Some(parse_quote!(u64)),
457 display: Default::None,
458 crate_path: None,
459 }
460 );
461 }
462
463 #[test]
464 fn parse_container_display() {
465 let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(display)])];
466 let ctx = Context::new();
467 let container = Container::from_ast(&ctx, &attrs);
468 ctx.check().unwrap();
469 assert_eq!(
470 container,
471 Container {
472 from_str: None,
473 type_from: None,
474 type_try_from: None,
475 type_into: None,
476 type_try_into: None,
477 display: Default::Default(parse_quote!(display)),
478 crate_path: None,
479 }
480 );
481 }
482
483 #[test]
484 fn parse_container_display_path() {
485 let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(display="Path::display")])];
486 let ctx = Context::new();
487 let container = Container::from_ast(&ctx, &attrs);
488 ctx.check().unwrap();
489 assert_eq!(
490 container,
491 Container {
492 from_str: None,
493 type_from: None,
494 type_try_from: None,
495 type_into: None,
496 type_try_into: None,
497 display: Default::Path(parse_quote!(Path::display)),
498 crate_path: None,
499 }
500 );
501 }
502}