1#![deny(unsafe_code)]
27#![deny(missing_docs)]
28#![doc(html_root_url = "https://docs.rs/nextjson-derive")]
29
30extern crate proc_macro;
31
32use proc_macro::{Delimiter, Spacing, TokenStream, TokenTree};
33use std::str::FromStr;
34
35mod attr;
36mod case;
37mod de;
38mod schema;
39mod ser;
40
41pub(crate) use attr::{ContainerAttrs, FieldAttrs, Meta, VariantAttrs};
42
43pub(crate) fn ts(s: &str) -> TokenStream {
45 TokenStream::from_str(s)
46 .unwrap_or_else(|e| panic!("nextjson-derive: invalid generated tokens: {e:?}"))
47}
48
49pub(crate) fn err(msg: &str) -> TokenStream {
51 ts(&format!("::core::compile_error!({:?});", msg))
52}
53
54pub(crate) fn err_str(msg: &str) -> String {
56 msg.to_string()
57}
58
59pub(crate) struct P<'a> {
61 pub toks: &'a [TokenTree],
62 pub i: usize,
63}
64
65impl<'a> P<'a> {
66 pub fn peek(&self) -> Option<&TokenTree> {
67 self.toks.get(self.i)
68 }
69 pub fn next(&mut self) -> Option<TokenTree> {
70 let t = self.toks.get(self.i).cloned();
71 if t.is_some() {
72 self.i += 1;
73 }
74 t
75 }
76 pub fn is_ident(&self, s: &str) -> bool {
77 matches!(self.peek(), Some(TokenTree::Ident(id)) if id.to_string() == s)
78 }
79 pub fn is_punct(&self, ch: char) -> bool {
80 matches!(self.peek(), Some(TokenTree::Punct(p)) if p.as_char() == ch)
81 }
82 pub fn eat_ident(&mut self, s: &str) -> bool {
83 if self.is_ident(s) {
84 self.i += 1;
85 true
86 } else {
87 false
88 }
89 }
90 pub fn eat_punct(&mut self, ch: char) -> bool {
91 if self.is_punct(ch) {
92 self.i += 1;
93 true
94 } else {
95 false
96 }
97 }
98 pub fn expect_ident(&mut self) -> Option<String> {
99 match self.next() {
100 Some(TokenTree::Ident(id)) => Some(id.to_string()),
101 _ => None,
102 }
103 }
104}
105
106pub(crate) fn join(toks: &[TokenTree]) -> String {
109 let mut s = String::new();
110 let mut no_space = false;
111 for t in toks {
112 if !s.is_empty() && !no_space {
113 s.push(' ');
114 }
115 no_space = false;
116 match t {
117 TokenTree::Punct(p) => {
118 s.push_str(&p.to_string());
119 no_space = p.spacing() == Spacing::Joint;
120 }
121 _ => s.push_str(&t.to_string()),
122 }
123 }
124 s
125}
126
127pub(crate) fn split_top(toks: &[TokenTree], sep: char) -> Vec<Vec<TokenTree>> {
132 let mut out: Vec<Vec<TokenTree>> = Vec::new();
133 let mut cur: Vec<TokenTree> = Vec::new();
134 let mut angle: usize = 0;
135 for tt in toks {
136 match tt {
137 TokenTree::Group(_) => cur.push(tt.clone()),
138 TokenTree::Punct(p) if p.as_char() == '<' => {
139 angle += 1;
140 cur.push(tt.clone());
141 }
142 TokenTree::Punct(p) if p.as_char() == '>' => {
143 angle = angle.saturating_sub(1);
144 cur.push(tt.clone());
145 }
146 TokenTree::Punct(p) if angle == 0 && p.as_char() == sep => {
147 out.push(std::mem::take(&mut cur));
148 }
149 _ => cur.push(tt.clone()),
150 }
151 }
152 if !cur.is_empty() {
153 out.push(cur);
154 }
155 if out.is_empty() {
156 out.push(Vec::new());
157 }
158 out
159}
160
161pub(crate) fn read_angle(p: &mut P) -> Option<Vec<TokenTree>> {
164 if !p.eat_punct('<') {
165 return None;
166 }
167 let mut depth = 1usize;
168 let mut out = Vec::new();
169 while let Some(tt) = p.next() {
170 match &tt {
171 TokenTree::Punct(c) if c.as_char() == '<' => {
172 depth += 1;
173 out.push(tt);
174 }
175 TokenTree::Punct(c)
176 if c.as_char() == '-'
177 && matches!(p.peek(), Some(TokenTree::Punct(n)) if n.as_char() == '>') =>
178 {
179 out.push(tt);
180 out.push(p.next().unwrap());
181 }
182 TokenTree::Punct(c) if c.as_char() == '>' => {
183 if depth == 1 {
184 return Some(out);
185 }
186 depth -= 1;
187 out.push(tt);
188 }
189 _ => out.push(tt),
190 }
191 }
192 None
193}
194
195#[derive(Clone, Copy, PartialEq, Eq)]
200pub(crate) enum ParamKind {
201 Lifetime,
202 Type,
203 Const,
204}
205
206#[derive(Clone)]
207pub(crate) struct GenericParam {
208 pub kind: ParamKind,
209 pub full: String,
210 pub name: String,
211}
212
213#[derive(Clone, Default)]
214pub(crate) struct Generics {
215 pub params: Vec<GenericParam>,
216 pub where_preds: Vec<String>,
217}
218
219#[derive(Clone)]
220pub(crate) struct Field {
221 pub ident: Option<String>,
222 pub ty: String,
223 pub attrs: Vec<attr::Meta>,
224}
225
226#[derive(Clone)]
227pub(crate) enum Fields {
228 Unit,
229 Named(Vec<Field>),
230 Unnamed(Vec<Field>),
231}
232
233impl Fields {
234 pub fn iter(&self) -> core::slice::Iter<'_, Field> {
235 match self {
236 Fields::Unit => [].iter(),
237 Fields::Named(fields) | Fields::Unnamed(fields) => fields.iter(),
238 }
239 }
240}
241
242#[derive(Clone)]
243pub(crate) struct Variant {
244 pub ident: String,
245 pub fields: Fields,
246 pub attrs: Vec<attr::Meta>,
247}
248
249#[derive(Clone)]
250pub(crate) enum Data {
251 Struct(Fields),
252 Enum(Vec<Variant>),
253}
254
255#[derive(Clone)]
256pub(crate) struct Input {
257 pub ident: String,
258 pub generics: Generics,
259 pub data: Data,
260 pub cattr: ContainerAttrs,
261}
262
263fn parse_attrs(p: &mut P) -> Vec<Vec<TokenTree>> {
269 let mut out = Vec::new();
270 while p.is_punct('#') {
271 p.next();
272 if let Some(TokenTree::Group(g)) = p.next() {
273 if g.delimiter() == Delimiter::Bracket {
274 out.push(g.stream().into_iter().collect());
275 }
276 }
277 }
278 out
279}
280
281fn collect_metas(groups: &[Vec<TokenTree>]) -> Vec<attr::Meta> {
283 let mut out = Vec::new();
284 for g in groups {
285 out.extend(attr::metas_from_attr(g));
286 }
287 out
288}
289
290pub(crate) fn parse_input(input: TokenStream) -> Result<Input, String> {
295 let toks: Vec<TokenTree> = input.into_iter().collect();
296 let mut p = P { toks: &toks, i: 0 };
297
298 let attrs = parse_attrs(&mut p);
299 let cattr = ContainerAttrs::from_metas(&collect_metas(&attrs));
300 eat_visibility(&mut p);
301
302 let is_enum = if p.eat_ident("struct") {
303 false
304 } else if p.eat_ident("enum") {
305 true
306 } else {
307 return Err("nextjson: expected `struct` or `enum`".into());
308 };
309
310 let ident = p
311 .expect_ident()
312 .ok_or_else(|| "nextjson: expected type name".to_string())?;
313
314 let mut generics = Generics::default();
315 if let Some(inner) = read_angle(&mut p) {
316 generics = parse_generics(&inner);
317 }
318
319 let data = if !is_enum
320 && matches!(p.peek(), Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis)
321 {
322 let Some(TokenTree::Group(body)) = p.next() else {
323 return Err("nextjson: expected a tuple struct body".into());
324 };
325 if p.eat_ident("where") {
326 parse_where_clause(&mut p, &mut generics, false);
327 }
328 p.eat_punct(';');
331 let inner: Vec<TokenTree> = body.stream().into_iter().collect();
332 Data::Struct(Fields::Unnamed(parse_unnamed_fields(&inner)))
333 } else {
334 if p.eat_ident("where") {
335 parse_where_clause(&mut p, &mut generics, true);
336 }
337 if !is_enum {
338 match p.next() {
339 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => {
340 let inner: Vec<TokenTree> = g.stream().into_iter().collect();
341 Data::Struct(Fields::Named(parse_named_fields(&inner)))
342 }
343 Some(TokenTree::Punct(pc)) if pc.as_char() == ';' => Data::Struct(Fields::Unit),
344 _ => return Err("nextjson: expected a struct body".into()),
345 }
346 } else {
347 match p.next() {
348 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => {
349 let inner: Vec<TokenTree> = g.stream().into_iter().collect();
350 Data::Enum(parse_variants(&inner))
351 }
352 _ => return Err("nextjson: expected an enum body".into()),
353 }
354 }
355 };
356
357 if p.i != toks.len() {
363 return Err(format!(
364 "nextjson: cannot parse trailing tokens: {}",
365 join(&toks[p.i..])
366 ));
367 }
368
369 Ok(Input {
370 ident,
371 generics,
372 data,
373 cattr,
374 })
375}
376
377fn parse_where_clause(p: &mut P<'_>, generics: &mut Generics, has_braced_body: bool) {
378 let mut tokens = Vec::new();
379 while let Some(token) = p.peek() {
380 let is_body = has_braced_body
381 && p.i + 1 == p.toks.len()
382 && matches!(token, TokenTree::Group(g) if g.delimiter() == Delimiter::Brace);
383 if is_body || matches!(token, TokenTree::Punct(punct) if punct.as_char() == ';') {
384 break;
385 }
386 if let Some(token) = p.next() {
387 tokens.push(token);
388 }
389 }
390 for piece in split_top(&tokens, ',') {
391 let predicate = join(&piece).trim().to_string();
392 if !predicate.is_empty() {
393 generics.where_preds.push(predicate);
394 }
395 }
396}
397
398fn parse_generics(inner: &[TokenTree]) -> Generics {
399 let mut g = Generics::default();
400 for item in split_top(inner, ',') {
401 if item.is_empty() {
402 continue;
403 }
404 let declaration = strip_generic_default(&item);
405 let mut p = P {
406 toks: &declaration,
407 i: 0,
408 };
409 if p.is_punct('\'') {
410 p.next();
411 let name = p.expect_ident().unwrap_or_default();
412 g.params.push(GenericParam {
413 kind: ParamKind::Lifetime,
414 full: join(&declaration),
415 name: format!("'{name}"),
416 });
417 } else if p.eat_ident("const") {
418 let name = p.expect_ident().unwrap_or_default();
419 g.params.push(GenericParam {
420 kind: ParamKind::Const,
421 full: join(&declaration),
422 name,
423 });
424 } else {
425 let name = p.expect_ident().unwrap_or_default();
426 g.params.push(GenericParam {
427 kind: ParamKind::Type,
428 full: join(&declaration),
429 name,
430 });
431 }
432 }
433 g
434}
435
436fn strip_generic_default(tokens: &[TokenTree]) -> Vec<TokenTree> {
437 let mut angle_depth = 0usize;
438 for (index, token) in tokens.iter().enumerate() {
439 match token {
440 TokenTree::Punct(punct) if punct.as_char() == '<' => angle_depth += 1,
441 TokenTree::Punct(punct) if punct.as_char() == '>' => {
442 angle_depth = angle_depth.saturating_sub(1);
443 }
444 TokenTree::Punct(punct) if punct.as_char() == '=' && angle_depth == 0 => {
445 return tokens[..index].to_vec();
446 }
447 _ => {}
448 }
449 }
450 tokens.to_vec()
451}
452
453fn parse_named_fields(inner: &[TokenTree]) -> Vec<Field> {
454 split_top(inner, ',')
455 .iter()
456 .filter(|s| !s.is_empty())
457 .map(|piece| parse_named_field(piece))
458 .collect()
459}
460
461pub(crate) fn eat_visibility(p: &mut P<'_>) {
466 if !p.eat_ident("pub") {
467 return;
468 }
469 if matches!(p.peek(), Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis) {
470 p.next();
471 }
472}
473
474fn parse_named_field(piece: &[TokenTree]) -> Field {
475 let mut p = P { toks: piece, i: 0 };
476 let attrs = parse_attrs(&mut p);
477 eat_visibility(&mut p);
478 let mut colon = None;
480 let mut j = p.i;
481 while j < piece.len() {
482 match &piece[j] {
483 TokenTree::Punct(c) if c.as_char() == ':' => {
484 if matches!(piece.get(j + 1), Some(TokenTree::Punct(n)) if n.as_char() == ':') {
485 j += 2;
486 continue;
487 }
488 colon = Some(j);
489 break;
490 }
491 _ => j += 1,
492 }
493 }
494 let (ident, ty) = match colon {
495 Some(c) => (
496 Some(join(&piece[p.i..c]).trim().to_string()),
497 join(&piece[c + 1..]).trim().to_string(),
498 ),
499 None => (None, join(&piece[p.i..]).trim().to_string()),
500 };
501 let mut field_metas = collect_metas(&attrs);
502 if crate::schema::is_phantom_data(&ty) {
506 field_metas.push(Meta::Flag("skip".to_string()));
507 }
508 Field {
509 ident,
510 ty,
511 attrs: field_metas,
512 }
513}
514
515fn parse_unnamed_fields(inner: &[TokenTree]) -> Vec<Field> {
516 split_top(inner, ',')
517 .iter()
518 .filter(|s| !s.is_empty())
519 .map(|piece| {
520 let mut p = P { toks: piece, i: 0 };
521 let attrs = parse_attrs(&mut p);
522 eat_visibility(&mut p);
523 Field {
524 ident: None,
525 ty: join(&piece[p.i..]).trim().to_string(),
526 attrs: collect_metas(&attrs),
527 }
528 })
529 .collect()
530}
531
532fn parse_variants(inner: &[TokenTree]) -> Vec<Variant> {
533 split_top(inner, ',')
534 .iter()
535 .filter(|s| !s.is_empty())
536 .map(|piece| {
537 let mut p = P { toks: piece, i: 0 };
538 let attrs = parse_attrs(&mut p);
539 let ident = p.expect_ident().unwrap_or_default();
540 let fields = match p.next() {
541 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => {
542 let inner2: Vec<TokenTree> = g.stream().into_iter().collect();
543 Fields::Named(parse_named_fields(&inner2))
544 }
545 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => {
546 let inner2: Vec<TokenTree> = g.stream().into_iter().collect();
547 Fields::Unnamed(parse_unnamed_fields(&inner2))
548 }
549 _ => Fields::Unit,
550 };
551 Variant {
552 ident,
553 fields,
554 attrs: collect_metas(&attrs),
555 }
556 })
557 .collect()
558}
559
560pub(crate) fn build_generics(
566 input: &Input,
567 cp: &str,
568 de: bool,
569 has_flatten: bool,
570 has_borrow: bool,
571) -> (String, String, String) {
572 let g = &input.generics;
573 let c = &input.cattr;
574 let name = input.ident.clone();
575 let (self_ty, remote_typed) = match &c.remote {
580 Some(r) => (r.clone(), true),
581 None => (name.clone(), false),
582 };
583
584 let mut impl_params: Vec<String> = g.params.iter().map(|p| p.full.clone()).collect();
585 if de {
586 impl_params.insert(0, "'de".to_string());
587 }
588 let impl_generics = if impl_params.is_empty() {
589 String::new()
590 } else {
591 format!("<{}>", impl_params.join(", "))
592 };
593
594 let names: Vec<String> = g.params.iter().map(|p| p.name.clone()).collect();
595 let ty_generics = if names.is_empty() {
596 String::new()
597 } else {
598 format!("<{}>", names.join(", "))
599 };
600 let self_ty_inst = if remote_typed {
604 self_ty.clone()
605 } else {
606 format!("{self_ty}{ty_generics}")
607 };
608
609 let mut preds: Vec<String> = g.where_preds.clone();
614
615 let directional = if de {
616 c.bound_de.as_ref()
617 } else {
618 c.bound_ser.as_ref()
619 };
620 let bound = directional.or(c.bound.as_ref());
621 let auto_bound = |p: &GenericParam| -> Option<String> {
622 if p.kind != ParamKind::Type {
623 return None;
624 }
625 if de && has_flatten {
626 Some(format!(
627 "{0}: for<'__n> {1}::NsonDeserialize<'__n>",
628 p.name, cp
629 ))
630 } else if de {
631 Some(format!("{}: {}::NsonDeserialize<'de>", p.name, cp))
632 } else {
633 Some(format!("{}: {}::NsonSerialize", p.name, cp))
634 }
635 };
636 if let Some(bound) = bound {
637 let cleaned = bound.trim().trim_matches('"');
638 if !cleaned.is_empty() {
639 for s in cleaned.split(',') {
640 let s = s.trim();
641 if !s.is_empty() {
642 preds.push(s.to_string());
643 }
644 }
645 }
646 } else {
647 for p in g.params.iter() {
648 if let Some(b) = auto_bound(p) {
649 preds.push(b);
650 }
651 }
652 }
653
654 if de && de_uses_type_param_default(input) {
660 for p in g.params.iter() {
661 if p.kind == ParamKind::Type {
662 preds.push(format!("{}: ::core::default::Default", p.name));
663 }
664 }
665 }
666
667 if de {
669 if let Some(from) = &c.from {
670 preds.push(format!(
671 "{from}: {cp}::NsonDeserialize<'de> + ::core::convert::Into<{self_ty_inst}>"
672 ));
673 }
674 if let Some(from) = &c.try_from {
675 preds.push(format!(
676 "{from}: {cp}::NsonDeserialize<'de> + ::core::convert::TryInto<{self_ty_inst}>"
677 ));
678 preds.push(format!(
679 "<{from} as ::core::convert::TryInto<{self_ty_inst}>>::Error: ::core::fmt::Display"
680 ));
681 }
682 } else {
683 if let Some(into) = &c.into {
684 preds.push(format!("{self_ty_inst}: ::core::clone::Clone"));
685 preds.push(format!("{self_ty_inst}: ::core::convert::Into<{into}>"));
686 preds.push(format!("{into}: {cp}::NsonSerialize"));
687 preds.push(format!("{into}: {cp}::NsonSchema"));
688 }
689 }
690
691 if de && has_borrow {
692 for p in g.params.iter() {
693 if p.kind == ParamKind::Lifetime {
694 preds.push(format!("'de: {}", p.name));
695 }
696 }
697 }
698
699 let where_clause = if preds.is_empty() {
700 String::new()
701 } else {
702 format!(" where {}", preds.join(", "))
703 };
704
705 (impl_generics, ty_generics, where_clause)
706}
707
708fn validate_input(input: &Input) -> Option<String> {
714 if let Some(name) = input.cattr.unknown.first() {
715 return Some(format!(
716 "nextjson: unsupported container attribute `{name}`; refusing to ignore wire semantics"
717 ));
718 }
719 fn unknown_field_attribute(fields: &Fields) -> Option<String> {
720 for field in fields.iter() {
721 if let Some(name) = attr::field_attrs(&field.attrs).unknown.into_iter().next() {
722 return Some(format!(
723 "nextjson: unsupported field attribute `{name}`; refusing to ignore wire semantics"
724 ));
725 }
726 }
727 None
728 }
729 match &input.data {
730 Data::Struct(fields) => {
731 if let Some(message) = unknown_field_attribute(fields) {
732 return Some(message);
733 }
734 }
735 Data::Enum(variants) => {
736 for variant in variants {
737 if let Some(name) = attr::variant_attrs(&variant.attrs)
738 .unknown
739 .into_iter()
740 .next()
741 {
742 return Some(format!(
743 "nextjson: unsupported variant attribute `{name}`; refusing to ignore wire semantics"
744 ));
745 }
746 if let Some(message) = unknown_field_attribute(&variant.fields) {
747 return Some(message);
748 }
749 }
750 }
751 }
752 if input.cattr.transparent {
754 match &input.data {
755 Data::Enum(_) => {
756 return Some("nextjson: `transparent` is not supported on enums".to_string());
757 }
758 Data::Struct(Fields::Named(f)) if f.len() != 1 => {
759 return Some("nextjson: `transparent` requires exactly one field".to_string());
760 }
761 Data::Struct(Fields::Unnamed(f)) if f.len() != 1 => {
762 return Some("nextjson: `transparent` requires exactly one field".to_string());
763 }
764 _ => {}
765 }
766 }
767 if type_has_flag_on(input, |f, fa| fa.flatten && f.ident.is_none()) {
770 return Some(
771 "nextjson: `flatten` is not allowed on tuple structs or tuple variants".to_string(),
772 );
773 }
774 if type_has_flag_on(input, |_f, fa| {
777 fa.flatten && fa.skip_serializing_if.is_some()
778 }) {
779 return Some(
780 "nextjson: `flatten` cannot be combined with `skip_serializing_if`".to_string(),
781 );
782 }
783 None
784}
785
786fn type_has_flag_on<F: Fn(&Field, &FieldAttrs) -> bool>(input: &Input, f: F) -> bool {
789 let check = |fld: &Field| f(fld, &attr::field_attrs(&fld.attrs));
790 match &input.data {
791 Data::Struct(fields) => fields.iter().any(check),
792 Data::Enum(variants) => variants.iter().any(|v| v.fields.iter().any(check)),
793 }
794}
795
796pub(crate) fn generate_impls(input: &Input) -> TokenStream {
798 if let Some(msg) = validate_input(input) {
799 return err(&msg);
800 }
801 let cp = input.cattr.crate_path.clone();
802 let name = input.ident.clone();
803 let (target, use_tg) = match &input.cattr.remote {
807 Some(r) => (r.clone(), false),
808 None => (name.clone(), true),
809 };
810 let (ig, tg, wc) = build_generics(input, &cp, false, false, false);
811 let tg_part = if use_tg { tg.as_str() } else { "" };
812 let body = if let Some(into) = &input.cattr.into {
813 format!(
815 "let __v: {into} = ::core::convert::Into::into(self.clone());\n\
816 <{into} as {cp}::NsonSerialize>::nextencode(&__v, __e)"
817 )
818 } else {
819 match &input.data {
820 Data::Struct(f) => ser::serialize_struct(&name, f, input, &cp),
821 Data::Enum(v) => ser::serialize_enum(&name, v, input, &cp),
822 }
823 };
824 let out = format!(
825 "#[automatically_derived]\n\
826 impl {ig} {cp}::NsonSchema for {target}{tg_part}{wc} {{\n\
827 \x20 const SCHEMA: {cp}::TypeSchema = {schema_expr};\n\
828 }}\n\
829 #[automatically_derived]\n\
830 impl {ig} {cp}::NsonSerialize for {target}{tg_part}{wc} {{\n\
831 \x20 fn nextencode<__E: {cp}::FormatEncoder>(&self, __e: &mut __E) -> ::core::result::Result<(), __E::Error> {{\n\
832 {body}\n\
833 \x20 }}\n\
834 }}",
835 schema_expr = schema::schema_expr(input, &cp)
836 );
837 ts(&out)
838}
839
840pub(crate) fn generate_de_impl(input: &Input) -> TokenStream {
842 if let Some(msg) = validate_input(input) {
843 return err(&msg);
844 }
845 let cp = input.cattr.crate_path.clone();
846 let name = input.ident.clone();
847 if matches!(&input.data, Data::Enum(_)) && input.cattr.has_default() {
851 return err("nextjson: `default` is not supported on enums");
852 }
853 let has_flatten = type_has_flag(input, |fa| fa.flatten);
854 let has_borrow = type_has_flag(input, |fa| fa.borrow);
855 if has_flatten && type_has_with(input) {
856 return err("nextjson: `flatten` cannot be combined with `with` / `deserialize_with`");
857 }
858 if has_flatten && input.cattr.deny_unknown_fields {
859 return err("nextjson: `deny_unknown_fields` cannot be combined with `flatten`");
862 }
863 let (target, use_tg) = match &input.cattr.remote {
864 Some(r) => (r.clone(), false),
865 None => (name.clone(), true),
866 };
867 let (ig, tg, wc) = build_generics(input, &cp, true, has_flatten, has_borrow);
868 let tg_part = if use_tg { tg.as_str() } else { "" };
869 let expecting = match &input.cattr.expecting {
872 Some(e) => format!("\n fn expecting() -> &'static str {{ {:?} }}\n", e),
873 None => String::new(),
874 };
875 let body = if let Some(from) = &input.cattr.from {
876 format!(
878 "let __v: {from} = <{from} as {cp}::NsonDeserialize<'de>>::nextdecode(__d)?;\n\
879 __out.write(::core::convert::Into::into(__v));\n\
880 ::core::result::Result::Ok(())"
881 )
882 } else if let Some(from) = &input.cattr.try_from {
883 format!(
885 "let __v: {from} = <{from} as {cp}::NsonDeserialize<'de>>::nextdecode(__d)?;\n\
886 let __c: Self = ::core::convert::TryInto::try_into(__v).map_err(|__e| {{\n\
887 \x20 {cp}::FormatError::custom({cp}::__private::ToString::to_string(&__e))\n\
888 }})?;\n\
889 __out.write(__c);\n\
890 ::core::result::Result::Ok(())"
891 )
892 } else {
893 match &input.data {
894 Data::Struct(f) => de::deserialize_struct(&name, f, input, &cp, has_flatten),
895 Data::Enum(v) => de::deserialize_enum(&name, v, input, &cp, has_flatten),
896 }
897 };
898 let out = format!(
899 "#[automatically_derived]\n\
900 impl {ig} {cp}::NsonDeserialize<'de> for {target}{tg_part}{wc} {{\n\
901 {expecting}\
902 \x20 fn nextdecode_into<__D: {cp}::FormatDecoder<'de>>(\n\
903 \x20 __d: &mut __D,\n\
904 \x20 __out: &mut {cp}::DecodeSlot<Self>,\n\
905 \x20 ) -> ::core::result::Result<(), __D::Error> {{\n\
906 \x20 __d.set_expecting(Self::expecting());\n\
907 {body}\n\
908 \x20 }}\n\
909 }}"
910 );
911 ts(&out)
912}
913
914fn type_has_flag<F: Fn(&FieldAttrs) -> bool>(input: &Input, f: F) -> bool {
915 match &input.data {
916 Data::Struct(fields) => fields.iter().any(|fld| f(&attr::field_attrs(&fld.attrs))),
917 Data::Enum(variants) => variants
918 .iter()
919 .any(|v| v.fields.iter().any(|fld| f(&attr::field_attrs(&fld.attrs)))),
920 }
921}
922
923fn de_uses_type_param_default(input: &Input) -> bool {
932 if input.cattr.has_default() {
933 return true;
934 }
935 let mut found = false;
936 let mut scan = |f: &Field| {
937 if crate::schema::is_phantom_data(&f.ty) {
938 return;
939 }
940 let fa = attr::field_attrs(&f.attrs);
941 let bare_default = fa.default == Some(String::new());
942 let skip_without_path =
943 fa.skip_deserializing && !matches!(&fa.default, Some(d) if !d.is_empty());
944 if bare_default || skip_without_path {
945 found = true;
946 }
947 };
948 match &input.data {
949 Data::Struct(fields) => {
950 for f in fields.iter() {
951 scan(f);
952 }
953 }
954 Data::Enum(variants) => {
955 for v in variants {
956 for f in v.fields.iter() {
957 scan(f);
958 }
959 }
960 }
961 }
962 found
963}
964
965fn type_has_with(input: &Input) -> bool {
966 type_has_flag(input, |fa| {
967 fa.with.is_some() || fa.deserialize_with.is_some()
968 })
969}
970
971#[proc_macro_derive(NsonSerialize, attributes(njson, nextjson, serde))]
976pub fn derive_serialize(input: TokenStream) -> TokenStream {
983 match parse_input(input) {
984 Ok(ast) => generate_impls(&ast),
985 Err(e) => err(&e),
986 }
987}
988
989#[proc_macro_derive(NsonDeserialize, attributes(njson, nextjson, serde))]
990pub fn derive_deserialize(input: TokenStream) -> TokenStream {
997 match parse_input(input) {
998 Ok(ast) => generate_de_impl(&ast),
999 Err(e) => err(&e),
1000 }
1001}
1002
1003#[cfg(test)]
1004mod tests {
1005 use super::*;
1006
1007 fn meta_flag(name: &str) -> Meta {
1014 Meta::Flag(name.to_string())
1015 }
1016 fn meta_named(name: &str, value: &str) -> Meta {
1017 Meta::Named(name.to_string(), value.to_string())
1018 }
1019 fn named_field(ident: &str, ty: &str, metas: Vec<Meta>) -> Field {
1020 Field {
1021 ident: Some(ident.to_string()),
1022 ty: ty.to_string(),
1023 attrs: metas,
1024 }
1025 }
1026 fn unnamed_field(ty: &str, metas: Vec<Meta>) -> Field {
1027 Field {
1028 ident: None,
1029 ty: ty.to_string(),
1030 attrs: metas,
1031 }
1032 }
1033 fn cattr(metas: &[Meta]) -> ContainerAttrs {
1034 ContainerAttrs::from_metas(metas)
1035 }
1036 fn struct_named(cattr: ContainerAttrs, fields: Vec<Field>) -> Input {
1037 Input {
1038 ident: "S".into(),
1039 generics: Generics::default(),
1040 data: Data::Struct(Fields::Named(fields)),
1041 cattr,
1042 }
1043 }
1044 fn generic_input(
1045 ident: &str,
1046 params: Vec<GenericParam>,
1047 where_preds: Vec<String>,
1048 data: Data,
1049 cattr: ContainerAttrs,
1050 ) -> Input {
1051 Input {
1052 ident: ident.to_string(),
1053 generics: Generics {
1054 params,
1055 where_preds,
1056 },
1057 data,
1058 cattr,
1059 }
1060 }
1061
1062 #[test]
1065 fn validate_rejects_transparent_enum() {
1066 let input = Input {
1067 ident: "E".into(),
1068 generics: Generics::default(),
1069 data: Data::Enum(vec![Variant {
1070 ident: "A".into(),
1071 fields: Fields::Unit,
1072 attrs: vec![],
1073 }]),
1074 cattr: cattr(&[meta_flag("transparent")]),
1075 };
1076 let msg = validate_input(&input).expect("must reject transparent enum");
1077 assert!(msg.contains("transparent"));
1078 }
1079
1080 #[test]
1081 fn validate_rejects_transparent_multi_field() {
1082 let input = struct_named(
1083 cattr(&[meta_flag("transparent")]),
1084 vec![
1085 named_field("a", "i32", vec![]),
1086 named_field("b", "i32", vec![]),
1087 ],
1088 );
1089 assert!(validate_input(&input).is_some());
1090 }
1091
1092 #[test]
1093 fn validate_rejects_flatten_on_tuple() {
1094 let input = Input {
1095 ident: "T".into(),
1096 generics: Generics::default(),
1097 data: Data::Struct(Fields::Unnamed(vec![unnamed_field(
1098 "std::collections::BTreeMap<String, i32>",
1099 vec![meta_flag("flatten")],
1100 )])),
1101 cattr: cattr(&[]),
1102 };
1103 let msg = validate_input(&input).expect("must reject flatten on tuple field");
1104 assert!(msg.contains("flatten"), "unexpected error: {msg}");
1105 }
1106
1107 #[test]
1108 fn validate_rejects_flatten_with_skip_if() {
1109 let input = struct_named(
1110 cattr(&[]),
1111 vec![named_field(
1112 "m",
1113 "std::collections::BTreeMap<String, i32>",
1114 vec![
1115 meta_flag("flatten"),
1116 meta_named("skip_serializing_if", "Option::is_none"),
1117 ],
1118 )],
1119 );
1120 let msg = validate_input(&input).expect("must reject flatten + skip_serializing_if");
1121 assert!(msg.contains("flatten"), "unexpected error: {msg}");
1122 }
1123
1124 #[test]
1125 fn validate_rejects_unknown_wire_attributes() {
1126 let input = struct_named(
1127 cattr(&[]),
1128 vec![named_field(
1129 "a",
1130 "i32",
1131 vec![meta_flag("not_actually_supported")],
1132 )],
1133 );
1134 let message = validate_input(&input).expect("unknown attribute must be rejected");
1135 assert!(message.contains("not_actually_supported"));
1136 assert!(message.contains("refusing to ignore"));
1137 }
1138
1139 #[test]
1140 fn validate_accepts_valid_combinations() {
1141 let ok = struct_named(
1142 cattr(&[]),
1143 vec![
1144 named_field("a", "i32", vec![]),
1145 named_field(
1146 "m",
1147 "std::collections::BTreeMap<String, i32>",
1148 vec![meta_flag("flatten")],
1149 ),
1150 ],
1151 );
1152 assert!(validate_input(&ok).is_none());
1153
1154 let w = Input {
1155 ident: "W".into(),
1156 generics: Generics::default(),
1157 data: Data::Struct(Fields::Unnamed(vec![unnamed_field("i32", vec![])])),
1158 cattr: cattr(&[meta_flag("transparent")]),
1159 };
1160 assert!(validate_input(&w).is_none());
1161 }
1162
1163 #[test]
1166 fn build_generics_keeps_where_clause_with_bound() {
1167 let input = generic_input(
1170 "S",
1171 vec![GenericParam {
1172 kind: ParamKind::Type,
1173 full: "T".into(),
1174 name: "T".into(),
1175 }],
1176 vec!["T: core::fmt::Debug".into()],
1177 Data::Struct(Fields::Named(vec![named_field("v", "T", vec![])])),
1178 cattr(&[meta_named("bound", "\"T: Clone\"")]),
1179 );
1180 let (_, _, wc) = build_generics(&input, "::nextjson", false, false, false);
1181 assert!(wc.contains("Clone"), "missing user bound: {wc}");
1182 assert!(wc.contains("Debug"), "missing struct where clause: {wc}");
1183 }
1184
1185 #[test]
1186 fn build_generics_instantiates_conversion_self_type() {
1187 let input = generic_input(
1189 "Dst",
1190 vec![GenericParam {
1191 kind: ParamKind::Type,
1192 full: "T".into(),
1193 name: "T".into(),
1194 }],
1195 vec![],
1196 Data::Struct(Fields::Named(vec![named_field("x", "T", vec![])])),
1197 cattr(&[meta_named("from", "\"Src<T>\"")]),
1198 );
1199 let (_, _, wc) = build_generics(&input, "::nextjson", true, false, false);
1200 assert!(
1201 wc.contains("Into<Dst<T>>"),
1202 "missing instantiated self: {wc}"
1203 );
1204 assert!(!wc.contains("Into<Dst>"), "bare self type: {wc}");
1205 }
1206
1207 #[test]
1208 fn build_generics_adds_default_bound_for_generic_default() {
1209 let input = generic_input(
1211 "S",
1212 vec![GenericParam {
1213 kind: ParamKind::Type,
1214 full: "T".into(),
1215 name: "T".into(),
1216 }],
1217 vec![],
1218 Data::Struct(Fields::Named(vec![named_field("v", "T", vec![])])),
1219 cattr(&[meta_flag("default")]),
1220 );
1221 let (_, _, wc) = build_generics(&input, "::nextjson", true, false, false);
1222 assert!(
1223 wc.contains("T: ::core::default::Default"),
1224 "missing Default bound: {wc}"
1225 );
1226 }
1227
1228 #[test]
1229 fn build_generics_remote_never_appends_generics_twice() {
1230 let input = generic_input(
1232 "Mirror",
1233 vec![GenericParam {
1234 kind: ParamKind::Type,
1235 full: "T".into(),
1236 name: "T".into(),
1237 }],
1238 vec![],
1239 Data::Struct(Fields::Named(vec![named_field("x", "T", vec![])])),
1240 cattr(&[meta_named("remote", "external::Foreign<T>")]),
1241 );
1242 let (_, _, wc) = build_generics(&input, "::nextjson", false, false, false);
1243 assert!(
1244 wc.contains("T: ::nextjson::NsonSerialize"),
1245 "missing auto bound: {wc}"
1246 );
1247 }
1248
1249 #[test]
1252 fn default_bound_not_needed_for_phantom_data() {
1253 let input = struct_named(
1255 cattr(&[]),
1256 vec![
1257 named_field("_m", "core::marker::PhantomData<T>", vec![]),
1258 named_field("n", "i32", vec![]),
1259 ],
1260 );
1261 assert!(!de_uses_type_param_default(&input));
1262
1263 let input2 = struct_named(
1265 cattr(&[meta_flag("default")]),
1266 vec![named_field("v", "T", vec![])],
1267 );
1268 assert!(de_uses_type_param_default(&input2));
1269 }
1270
1271 #[test]
1274 fn phantom_detection_spellings() {
1275 assert!(crate::schema::is_phantom_data("PhantomData<T>"));
1276 assert!(crate::schema::is_phantom_data(
1277 "core::marker::PhantomData<T>"
1278 ));
1279 assert!(crate::schema::is_phantom_data(
1280 "::core::marker::PhantomData<T>"
1281 ));
1282 assert!(crate::schema::is_phantom_data(
1283 "std::marker::PhantomData<T>"
1284 ));
1285 assert!(!crate::schema::is_phantom_data("Vec<T>"));
1286 assert!(!crate::schema::is_phantom_data("Option<PhantomData<T>>"));
1287 assert!(!crate::schema::is_phantom_data("Phantom"));
1288 }
1289}