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