1#![warn(rust_2018_idioms, clippy::all)]
6#![deny(clippy::correctness)]
7#![allow(clippy::match_like_matches_macro)]
8
9use darling::{
10 ast,
11 util::{Ignored, SpannedValue},
12 FromDeriveInput, FromField, FromMeta, FromVariant,
13};
14use proc_macro2::Span;
15use proc_macro_error2::{abort, proc_macro_error};
16use quote::{format_ident, quote};
17use std::{ops::Deref, str::FromStr};
18use syn::{
19 ext::IdentExt,
20 parse::Parser,
21 parse_quote, parse_str,
22 punctuated::Punctuated,
23 visit_mut::{self, VisitMut},
24 AngleBracketedGenericArguments, Attribute, DeriveInput, Expr, ExprLit,
25 GenericArgument, GenericParam, Generics, Ident, Item, ItemImpl, ItemStruct,
26 Lifetime, Lit, LitStr, Meta, MetaNameValue, Path, PathArguments,
27 PathSegment, PredicateLifetime, PredicateType, Token, TraitBound,
28 TraitBoundModifier, Type, TypeParam, TypeParamBound, TypePath, WhereClause,
29 WherePredicate,
30};
31
32#[proc_macro_error]
33#[proc_macro_derive(TypeDef, attributes(type_def, serde))]
34pub fn derive_type_def(
35 input: proc_macro::TokenStream,
36) -> proc_macro::TokenStream {
37 let input = match syn::parse2::<DeriveInput>(input.into()) {
38 Ok(data) => data,
39 Err(err) => return err.to_compile_error().into(),
40 };
41 let mut input = match TypeDefInput::from_derive_input(&input) {
42 Ok(input) => input,
43 Err(error) => return error.write_errors().into(),
44 };
45
46 remove_skipped(&mut input.data);
47
48 let generics: &mut Generics = &mut input.generics;
49 if generics.params.iter().any(|param| match param {
50 GenericParam::Type(_) | GenericParam::Lifetime(_) => true,
51 _ => false,
52 }) {
53 generics
55 .where_clause
56 .get_or_insert_with(|| WhereClause {
57 where_token: <Token![where]>::default(),
58 predicates: Punctuated::new(),
59 })
60 .predicates
61 .extend(generics.params.iter().filter_map(|param| {
62 match param {
63 GenericParam::Type(param) => {
64 Some(WherePredicate::Type(PredicateType {
66 lifetimes: None,
67 bounded_ty: Type::Path(TypePath {
68 qself: None,
69 path: ident_path(param.ident.clone()),
70 }),
71 colon_token: <Token![:]>::default(),
72 bounds: IntoIterator::into_iter([
73 TypeParamBound::Trait(TraitBound {
74 paren_token: None,
75 modifier: TraitBoundModifier::None,
76 lifetimes: None,
77 path: Path::parse_mod_style
78 .parse_str(
79 "::typescript_type_def::TypeDef",
80 )
81 .unwrap(),
82 }),
83 ])
84 .collect(),
85 }))
86 }
87 GenericParam::Lifetime(param) => {
88 Some(WherePredicate::Lifetime(PredicateLifetime {
90 lifetime: param.lifetime.clone(),
91 colon_token: <Token![:]>::default(),
92 bounds: std::iter::once(Lifetime::new(
93 "'static",
94 Span::call_site(),
95 ))
96 .collect(),
97 }))
98 }
99 GenericParam::Const(_) => None,
100 }
101 }));
102 }
103
104 let ty_name = &input.ident;
105
106 let (impl_generics, ty_generics, where_clause) =
107 input.generics.split_for_impl();
108
109 let info_def = make_info_def(&input);
110
111 (quote! {
112 impl #impl_generics ::typescript_type_def::TypeDef for
113 #ty_name #ty_generics
114 #where_clause
115 {
116 const INFO: ::typescript_type_def::type_expr::TypeInfo = #info_def;
117 }
118 })
119 .into()
120}
121
122#[derive(FromDeriveInput)]
123#[darling(attributes(type_def, serde), forward_attrs)]
124struct TypeDefInput {
125 attrs: Vec<Attribute>,
126 ident: Ident,
127 generics: Generics,
128 data: ast::Data<TypeDefVariant, TypeDefField>,
129
130 #[darling(default)]
132 namespace: Namespace,
133
134 #[darling(default)]
136 tag: Option<SpannedValue<String>>,
137 #[darling(default)]
138 content: Option<SpannedValue<String>>,
139 #[darling(default)]
140 untagged: SpannedValue<Flag>,
141 #[darling(default)]
142 rename_all: Option<SpannedValue<String>>,
143 #[darling(default)]
144 rename_all_fields: Option<SpannedValue<String>>,
145 #[darling(default)]
146 rename: Option<SpannedValue<String>>,
147 #[darling(default)]
148 #[allow(dead_code)] transparent: Ignored,
150 #[darling(default)]
151 #[allow(dead_code)]
152 deny_unknown_fields: Ignored,
153 #[darling(default)]
154 #[allow(dead_code)]
155 bound: Ignored,
156 #[darling(default)]
157 #[allow(dead_code)]
158 default: Ignored,
159 #[darling(default)]
160 #[allow(dead_code)]
161 remote: Ignored,
162 #[darling(default)]
163 #[allow(dead_code)]
164 from: Ignored,
165 #[darling(default)]
166 #[allow(dead_code)]
167 try_from: Ignored,
168 #[darling(default)]
169 #[allow(dead_code)]
170 into: Ignored,
171 #[darling(default, rename = "crate")]
172 #[allow(dead_code)]
173 crate_: Ignored,
174}
175
176#[derive(FromField)]
177#[darling(attributes(type_def, serde), forward_attrs)]
178struct TypeDefField {
179 attrs: Vec<Attribute>,
180 ident: Option<Ident>,
181 ty: Type,
182
183 #[darling(default)]
185 type_of: Option<SpannedValue<TypeFromMeta>>,
186
187 #[darling(default)]
189 flatten: SpannedValue<Flag>,
190 #[darling(default)]
191 skip_serializing_if: Option<SpannedValue<String>>,
192 #[darling(default)]
193 default: SpannedValue<FieldDefault>,
194 #[darling(default)]
195 skip: SpannedValue<Flag>,
196 #[darling(default)]
197 rename: Option<SpannedValue<String>>,
198 #[darling(default)]
199 #[allow(dead_code)]
200 alias: Ignored,
201 #[darling(default)]
202 #[allow(dead_code)]
203 skip_serializing: Ignored,
204 #[darling(default)]
205 #[allow(dead_code)]
206 skip_deserializing: Ignored,
207 #[darling(default)]
208 #[allow(dead_code)]
209 serialize_with: Ignored,
210 #[darling(default)]
211 #[allow(dead_code)]
212 deserialize_with: Ignored,
213 #[darling(default)]
214 #[allow(dead_code)]
215 with: Ignored,
216 #[darling(default)]
217 #[allow(dead_code)]
218 borrow: Ignored,
219 #[darling(default)]
220 #[allow(dead_code)]
221 bound: Ignored,
222 #[darling(default)]
223 #[allow(dead_code)]
224 getter: Ignored,
225}
226
227#[derive(FromVariant)]
228#[darling(attributes(serde), forward_attrs)]
229struct TypeDefVariant {
230 attrs: Vec<Attribute>,
231 ident: Ident,
232 fields: ast::Fields<TypeDefField>,
233
234 #[darling(default)]
236 rename_all: Option<SpannedValue<String>>,
237 #[darling(default)]
238 skip: SpannedValue<Flag>,
239 #[darling(default)]
240 rename: Option<SpannedValue<String>>,
241 #[darling(default)]
242 #[allow(dead_code)]
243 alias: Ignored,
244 #[darling(default)]
245 #[allow(dead_code)]
246 skip_serializing: Ignored,
247 #[darling(default)]
248 #[allow(dead_code)]
249 skip_deserializing: Ignored,
250 #[darling(default)]
251 #[allow(dead_code)]
252 serialize_with: Ignored,
253 #[darling(default)]
254 #[allow(dead_code)]
255 deserialize_with: Ignored,
256 #[darling(default)]
257 #[allow(dead_code)]
258 with: Ignored,
259 #[darling(default)]
260 #[allow(dead_code)]
261 bound: Ignored,
262 #[darling(default)]
263 #[allow(dead_code)]
264 borrow: Ignored,
265 #[darling(default)]
266 #[allow(dead_code)]
267 other: Ignored,
268}
269
270#[derive(Default)]
271struct Flag(bool);
272
273#[derive(Default)]
274struct Namespace {
275 parts: Vec<Ident>,
276}
277
278#[derive(Default)]
279struct FieldDefault(bool);
280
281struct TypeFromMeta(Type);
282
283fn make_info_def(
284 TypeDefInput {
285 attrs,
286 ident: ty_name,
287 generics,
288 data,
289 namespace,
290 tag,
291 content,
292 untagged,
293 rename_all,
294 rename_all_fields,
295 rename,
296 ..
297 }: &TypeDefInput,
298) -> Expr {
299 let type_param_decls =
300 generics.type_params().flat_map(|TypeParam { ident, .. }| {
301 let struct_name = format_ident!("__TypeParam_{}", ident);
302 let struct_decl: ItemStruct = parse_quote! {
303 #[allow(non_camel_case_types)]
304 struct #struct_name;
305 };
306 let r#ref = type_expr_ident(&ident.to_string());
307 let type_def_impl: ItemImpl = parse_quote! {
308 impl ::typescript_type_def::TypeDef for #struct_name {
309 const INFO: ::typescript_type_def::type_expr::TypeInfo =
310 ::typescript_type_def::type_expr::TypeInfo::Native(
311 ::typescript_type_def::type_expr::NativeTypeInfo {
312 r#ref: #r#ref,
313 },
314 );
315 }
316 };
317 [Item::Struct(struct_decl), Item::Impl(type_def_impl)]
318 });
319 let type_info = type_info(
320 namespace
321 .parts
322 .iter()
323 .map(|part| type_ident(&part.to_string())),
324 &match rename {
325 Some(rename) => type_ident(rename.as_str()),
326 None => type_ident(&ty_name.unraw().to_string()),
327 },
328 &match data {
329 ast::Data::Struct(ast::Fields { fields, style, .. }) => {
330 if let Some(tag) = tag {
331 abort!(tag.span(), "`tag` option is only valid for enums");
332 }
333 if let Some(content) = content {
334 abort!(
335 content.span(),
336 "`content` option is only valid for enums"
337 );
338 }
339 if ***untagged {
340 abort!(
341 untagged.span(),
342 "`untagged` option is only valid for enums"
343 );
344 }
345
346 match style {
347 ast::Style::Unit => type_expr_ident("null"),
348 ast::Style::Tuple => fields_to_type_expr(
349 fields,
350 false,
351 rename_all.as_ref(),
352 generics,
353 None,
354 ),
355 ast::Style::Struct => {
356 if fields.is_empty() {
357 type_expr_object([], None)
358 } else {
359 fields_to_type_expr(
360 fields,
361 true,
362 rename_all.as_ref(),
363 generics,
364 None,
365 )
366 }
367 }
368 }
369 }
370 ast::Data::Enum(variants) => variants_to_type_expr(
371 variants,
372 tag,
373 content,
374 untagged,
375 rename_all,
376 rename_all_fields,
377 generics,
378 ),
379 },
380 generics
381 .type_params()
382 .map(|TypeParam { ident, .. }| type_ident(&ident.to_string())),
383 generics.type_params().map(|TypeParam { ident, .. }| {
384 type_expr_ref(
385 &Type::Path(TypePath {
386 qself: None,
387 path: ident_path(ident.clone()),
388 }),
389 None,
390 )
391 }),
392 extract_type_docs(attrs).as_ref(),
393 );
394 parse_quote! {{
395 #(#type_param_decls)*
396 #type_info
397 }}
398}
399
400fn fields_to_type_expr(
401 fields: &[TypeDefField],
402 named: bool,
403 rename_all: Option<&SpannedValue<String>>,
404 generics: &Generics,
405 docs: Option<&Expr>,
406) -> Expr {
407 if fields.is_empty() {
408 return if named {
409 type_expr_object(std::iter::empty(), docs)
410 } else {
411 type_expr_tuple(std::iter::empty(), docs)
412 };
413 }
414 let all_flatten = fields.iter().all(|TypeDefField { flatten, .. }| {
415 if ***flatten && !named {
416 abort!(flatten.span(), "tuple fields cannot be flattened");
417 }
418 ***flatten
419 });
420 let flatten_exprs = fields
421 .iter()
422 .filter(|&TypeDefField { flatten, .. }| ***flatten)
423 .map(|TypeDefField { ty, type_of, .. }| {
424 let ty = if let Some(type_of) = type_of {
425 &***type_of
426 } else {
427 ty
428 };
429 type_expr_ref(ty, Some(generics))
430 });
431 let exprs = flatten_exprs.chain((!all_flatten).then(|| {
433 let fields = fields.iter().filter_map(
435 |TypeDefField {
436 attrs,
437 ident: field_name,
438 ty,
439 type_of,
440 flatten,
441 skip_serializing_if,
442 default,
443 rename,
444 ..
445 }| {
446 if ***flatten {
447 if !named {
448 abort!(
449 flatten.span(),
450 "tuple fields cannot be flattened"
451 );
452 }
453 return None;
454 }
455 let ty = if let Some(type_of) = type_of {
456 &***type_of
457 } else {
458 ty
459 };
460 if let Some(field_name) = field_name {
461 let name = type_string(
462 &serde_rename_ident(
463 field_name, rename, rename_all, true,
464 )
465 .value(),
466 None,
467 );
468 let mut ty = ty;
469 let optional = if let Some(skip_serializing_if) =
470 skip_serializing_if
471 {
472 if let Some(inner_ty) = is_option(ty) {
473 if parse_str::<Path>(skip_serializing_if).unwrap()
474 == parse_str::<Path>("Option::is_none").unwrap()
475 {
476 ty = inner_ty;
477 }
478 }
479 true
480 } else {
481 ***default
482 };
483 let r#type = type_expr_ref(ty, Some(generics));
484 Some(type_object_field(
485 &name,
486 optional,
487 &r#type,
488 extract_type_docs(attrs).as_ref(),
489 ))
490 } else {
491 Some(type_expr_ref(ty, Some(generics)))
492 }
493 },
494 );
495 if named {
496 type_expr_object(fields, docs)
497 } else {
498 type_expr_tuple(fields, docs)
499 }
500 }));
501 type_expr_intersection(exprs, None)
502}
503
504fn variants_to_type_expr(
505 variants: &[TypeDefVariant],
506 tag: &Option<SpannedValue<String>>,
507 content: &Option<SpannedValue<String>>,
508 untagged: &SpannedValue<Flag>,
509 variant_rename_all: &Option<SpannedValue<String>>,
510 fields_rename_all: &Option<SpannedValue<String>>,
511 generics: &Generics,
512) -> Expr {
513 type_expr_union(
514 variants.iter().map(
515 |TypeDefVariant {
516 attrs,
517 ident: variant_name,
518 fields: ast::Fields { style, fields, .. },
519 rename_all: field_rename_all,
520 rename: variant_rename,
521 ..
522 }| {
523 let variant_name = serde_rename_ident(
524 variant_name,
525 variant_rename,
526 variant_rename_all.as_ref(),
527 false,
528 );
529 let field_rename_all =
530 field_rename_all.as_ref().or(fields_rename_all.as_ref());
531 match (tag, content, ***untagged) {
532 (None, None, false) => match style {
533 ast::Style::Unit => type_expr_string(
534 &variant_name.value(),
535 extract_type_docs(attrs).as_ref(),
536 ),
537 ast::Style::Tuple | ast::Style::Struct => {
538 type_expr_object(
539 [type_object_field(
540 &type_string(&variant_name.value(), None),
541 false,
542 &fields_to_type_expr(
543 fields,
544 matches!(style, ast::Style::Struct),
545 field_rename_all,
546 generics,
547 None,
548 ),
549 extract_type_docs(attrs).as_ref(),
550 )],
551 None,
552 )
553 }
554 },
555 (None, None, true) => match style {
556 ast::Style::Unit => type_expr_ident("null"),
557 ast::Style::Tuple | ast::Style::Struct => {
558 fields_to_type_expr(
559 fields,
560 matches!(style, ast::Style::Struct),
561 field_rename_all,
562 generics,
563 extract_type_docs(attrs).as_ref(),
564 )
565 }
566 },
567 (Some(tag), None, false) => match style {
568 ast::Style::Unit => type_expr_object(
569 [type_object_field(
570 &type_string(tag, None),
571 false,
572 &type_expr_string(&variant_name.value(), None),
573 extract_type_docs(attrs).as_ref(),
574 )],
575 None,
576 ),
577 ast::Style::Tuple | ast::Style::Struct => {
578 if matches!(style, ast::Style::Tuple)
579 && fields.len() != 1
580 {
581 abort!(
582 tag.span(),
583 "cannot tag enums with tuple variants"
584 );
585 }
586 type_expr_intersection(
587 [
588 type_expr_object(
589 [type_object_field(
590 &type_string(tag, None),
591 false,
592 &type_expr_string(
593 &variant_name.value(),
594 None,
595 ),
596 extract_type_docs(attrs).as_ref(),
597 )],
598 None,
599 ),
600 fields_to_type_expr(
601 fields,
602 matches!(style, ast::Style::Struct),
603 field_rename_all,
604 generics,
605 None,
606 ),
607 ],
608 None,
609 )
610 }
611 },
612 (Some(tag), Some(content), false) => match style {
613 ast::Style::Unit => type_expr_object(
614 [type_object_field(
615 &type_string(tag, None),
616 false,
617 &type_expr_string(&variant_name.value(), None),
618 extract_type_docs(attrs).as_ref(),
619 )],
620 None,
621 ),
622 ast::Style::Tuple | ast::Style::Struct => {
623 type_expr_object(
624 [
625 type_object_field(
626 &type_string(tag, None),
627 false,
628 &type_expr_string(
629 &variant_name.value(),
630 None,
631 ),
632 extract_type_docs(attrs).as_ref(),
633 ),
634 type_object_field(
635 &type_string(content, None),
636 false,
637 &fields_to_type_expr(
638 fields,
639 matches!(style, ast::Style::Struct),
640 field_rename_all,
641 generics,
642 None,
643 ),
644 None,
645 ),
646 ],
647 None,
648 )
649 }
650 },
651 (Some(tag), _, true) => {
652 abort!(
653 tag.span(),
654 "cannot give both `tag` and `untagged` options"
655 );
656 }
657 (None, Some(content), _) => {
658 abort!(
659 content.span(),
660 "`content` option requires `tag` option"
661 );
662 }
663 }
664 },
665 ),
666 None,
667 )
668}
669
670fn type_ident(ident: &str) -> Expr {
671 parse_quote! {
672 ::typescript_type_def::type_expr::Ident(
673 #ident,
674 )
675 }
676}
677
678fn type_string(value: &str, docs: Option<&Expr>) -> Expr {
679 let docs = wrap_optional_docs(docs);
680 parse_quote! {
681 ::typescript_type_def::type_expr::TypeString {
682 docs: #docs,
683 value: #value,
684 }
685 }
686}
687
688fn type_expr_ident(ident: &str) -> Expr {
689 parse_quote! {
690 ::typescript_type_def::type_expr::TypeExpr::ident(
691 ::typescript_type_def::type_expr::Ident(
692 #ident,
693 ),
694 )
695 }
696}
697
698fn type_expr_ref(ty: &Type, generics: Option<&Generics>) -> Expr {
699 let mut ty = ty.clone();
700
701 if let Some(generics) = generics {
702 struct TypeParamReplace<'a> {
703 generics: &'a Generics,
704 }
705
706 impl VisitMut for TypeParamReplace<'_> {
707 fn visit_type_path_mut(&mut self, type_path: &mut TypePath) {
708 let TypePath { path, .. } = type_path;
709 if let Some(TypeParam { ident, .. }) = self
710 .generics
711 .type_params()
712 .find(|TypeParam { ident, .. }| path.is_ident(ident))
713 {
714 *path = ident_path(format_ident!("__TypeParam_{}", ident));
715 }
716
717 visit_mut::visit_type_path_mut(self, type_path);
718 }
719 }
720
721 visit_mut::visit_type_mut(&mut TypeParamReplace { generics }, &mut ty);
722 }
723
724 parse_quote! {
725 ::typescript_type_def::type_expr::TypeExpr::Ref(
726 &<#ty as ::typescript_type_def::TypeDef>::INFO,
727 )
728 }
729}
730
731fn type_expr_string(value: &str, docs: Option<&Expr>) -> Expr {
732 let docs = wrap_optional_docs(docs);
733 parse_quote! {
734 ::typescript_type_def::type_expr::TypeExpr::String(
735 ::typescript_type_def::type_expr::TypeString {
736 docs: #docs,
737 value: #value,
738 },
739 )
740 }
741}
742
743fn type_expr_tuple(
744 exprs: impl IntoIterator<Item = Expr>,
745 docs: Option<&Expr>,
746) -> Expr {
747 let docs = wrap_optional_docs(docs);
748 let exprs = exprs.into_iter().collect::<Vec<_>>();
749 if exprs.len() == 1 {
750 exprs.into_iter().next().unwrap()
751 } else {
752 parse_quote! {
753 ::typescript_type_def::type_expr::TypeExpr::Tuple(
754 ::typescript_type_def::type_expr::TypeTuple {
755 docs: #docs,
756 elements: &[#(#exprs,)*],
757 },
758 )
759 }
760 }
761}
762
763fn type_object_field(
764 name: &Expr,
765 optional: bool,
766 r#type: &Expr,
767 docs: Option<&Expr>,
768) -> Expr {
769 let docs = wrap_optional_docs(docs);
770 parse_quote! {
771 ::typescript_type_def::type_expr::ObjectField {
772 docs: #docs,
773 name: #name,
774 optional: #optional,
775 r#type: #r#type,
776 }
777 }
778}
779
780fn type_expr_object(
781 exprs: impl IntoIterator<Item = Expr>,
782 docs: Option<&Expr>,
783) -> Expr {
784 let docs = wrap_optional_docs(docs);
785 let exprs = exprs.into_iter();
786 parse_quote! {
787 ::typescript_type_def::type_expr::TypeExpr::Object(
788 ::typescript_type_def::type_expr::TypeObject {
789 docs: #docs,
790 index_signature: ::core::option::Option::None,
791 fields: &[#(#exprs,)*],
792 },
793 )
794 }
795}
796
797fn type_expr_union(
798 exprs: impl IntoIterator<Item = Expr>,
799 docs: Option<&Expr>,
800) -> Expr {
801 let docs = wrap_optional_docs(docs);
802 let exprs = exprs.into_iter().collect::<Vec<_>>();
803 if exprs.len() == 1 {
804 exprs.into_iter().next().unwrap()
805 } else {
806 parse_quote! {
807 ::typescript_type_def::type_expr::TypeExpr::Union(
808 ::typescript_type_def::type_expr::TypeUnion {
809 docs: #docs,
810 members: &[#(#exprs,)*],
811 },
812 )
813 }
814 }
815}
816
817fn type_expr_intersection(
818 exprs: impl IntoIterator<Item = Expr>,
819 docs: Option<&Expr>,
820) -> Expr {
821 let docs = wrap_optional_docs(docs);
822 let exprs = exprs.into_iter().collect::<Vec<_>>();
823 if exprs.len() == 1 {
824 exprs.into_iter().next().unwrap()
825 } else {
826 parse_quote! {
827 ::typescript_type_def::type_expr::TypeExpr::Intersection(
828 ::typescript_type_def::type_expr::TypeIntersection {
829 docs: #docs,
830 members: &[#(#exprs,)*],
831 },
832 )
833 }
834 }
835}
836
837fn type_info(
838 path_parts: impl IntoIterator<Item = Expr>,
839 name: &Expr,
840 def: &Expr,
841 generic_vars: impl IntoIterator<Item = Expr>,
842 generic_args: impl IntoIterator<Item = Expr>,
843 docs: Option<&Expr>,
844) -> Expr {
845 let docs = wrap_optional_docs(docs);
846 let path_parts = path_parts.into_iter();
847 let generic_vars = generic_vars.into_iter();
848 let generic_args = generic_args.into_iter();
849 parse_quote! {
850 ::typescript_type_def::type_expr::TypeInfo::Defined(
851 ::typescript_type_def::type_expr::DefinedTypeInfo {
852 def: ::typescript_type_def::type_expr::TypeDefinition {
853 docs: #docs,
854 path: &[#(#path_parts,)*],
855 name: #name,
856 generic_vars: &[#(#generic_vars,)*],
857 def: #def,
858 },
859 generic_args: &[#(#generic_args,)*],
860 },
861 )
862 }
863}
864
865fn extract_type_docs(attrs: &[Attribute]) -> Option<Expr> {
866 let mut lines = attrs
867 .iter()
868 .filter_map(|attr| {
869 if let Meta::NameValue(MetaNameValue {
870 path,
871 eq_token: _,
872 value,
873 }) = &attr.meta
874 {
875 if path.is_ident("doc") {
876 if let Expr::Lit(ExprLit {
877 attrs: _,
878 lit: Lit::Str(lit_str),
879 }) = value
880 {
881 return Some(lit_str.value());
882 }
883 }
884 }
885 None
886 })
887 .collect::<Vec<_>>();
888 let min_indent = lines
889 .iter()
890 .filter_map(|line| {
891 if line.is_empty() {
892 None
893 } else {
894 Some(
895 line.find(|c: char| !c.is_whitespace())
896 .unwrap_or(line.len()),
897 )
898 }
899 })
900 .min()?;
901 if min_indent > 0 {
902 for line in &mut lines {
903 if !line.is_empty() {
904 *line = line.split_off(min_indent);
905 }
906 }
907 }
908 let docs = lines.join("\n");
909 Some(parse_quote! {
910 ::typescript_type_def::type_expr::Docs(
911 #docs,
912 )
913 })
914}
915
916fn wrap_optional_docs(docs: Option<&Expr>) -> Expr {
917 match docs {
918 Some(docs) => parse_quote! {
919 ::core::option::Option::Some(
920 #docs,
921 )
922 },
923 None => parse_quote! {
924 ::core::option::Option::None
925 },
926 }
927}
928
929fn serde_rename_ident(
930 ident: &Ident,
931 rename: &Option<SpannedValue<String>>,
932 rename_all: Option<&SpannedValue<String>>,
933 is_field: bool,
934) -> LitStr {
935 let span = ident.span();
936 if let Some(rename) = rename {
937 LitStr::new(rename.as_str(), span)
938 } else {
939 let ident = ident.unraw().to_string();
940 let ident = if let Some(rename_all) = rename_all {
941 match rename_all.as_str() {
942 "lowercase" => ident.to_lowercase(),
943 "UPPERCASE" => ident.to_uppercase(),
944 _ => match ident_case::RenameRule::from_str(rename_all) {
945 Ok(rename_all) => match is_field {
946 true => rename_all.apply_to_field(ident),
947 false => rename_all.apply_to_variant(ident),
948 },
949 Err(()) => {
950 abort!(rename_all.span(), "unknown case conversion")
951 }
952 },
953 }
954 } else {
955 ident
956 };
957 LitStr::new(&ident, span)
958 }
959}
960
961fn remove_skipped(data: &mut ast::Data<TypeDefVariant, TypeDefField>) {
962 match data {
963 ast::Data::Struct(ast::Fields { fields, .. }) => {
964 remove_if(fields, |TypeDefField { skip, .. }| ***skip);
965 }
966 ast::Data::Enum(variants) => {
967 remove_if(
968 variants,
969 |TypeDefVariant {
970 fields: ast::Fields { fields, .. },
971 skip,
972 ..
973 }| {
974 if ***skip {
975 return true;
976 }
977 remove_if(fields, |TypeDefField { skip, .. }| ***skip);
978 false
979 },
980 );
981 }
982 }
983}
984
985impl Deref for Flag {
986 type Target = bool;
987
988 fn deref(&self) -> &Self::Target {
989 &self.0
990 }
991}
992
993impl FromMeta for Flag {
994 fn from_word() -> Result<Self, darling::Error> {
995 Ok(Self(true))
996 }
997}
998
999impl FromMeta for Namespace {
1000 fn from_value(value: &Lit) -> Result<Self, darling::Error> {
1001 match value {
1002 Lit::Str(lit_str) => Ok(Self {
1003 parts: lit_str
1004 .value()
1005 .split('.')
1006 .map(|part| format_ident!("{}", part))
1007 .collect(),
1008 }),
1009 _ => Err(darling::Error::custom("expected string literal")),
1010 }
1011 }
1012}
1013
1014impl Deref for FieldDefault {
1015 type Target = bool;
1016
1017 fn deref(&self) -> &Self::Target {
1018 &self.0
1019 }
1020}
1021
1022impl FromMeta for FieldDefault {
1023 fn from_word() -> Result<Self, darling::Error> {
1024 Ok(Self(true))
1025 }
1026
1027 fn from_string(_value: &str) -> Result<Self, darling::Error> {
1028 Ok(Self(true))
1029 }
1030}
1031
1032impl Deref for TypeFromMeta {
1033 type Target = Type;
1034
1035 fn deref(&self) -> &Self::Target {
1036 &self.0
1037 }
1038}
1039
1040impl FromMeta for TypeFromMeta {
1041 fn from_string(value: &str) -> Result<Self, darling::Error> {
1042 parse_str(value).map(Self).map_err(Into::into)
1043 }
1044}
1045
1046fn is_option(ty: &Type) -> Option<&Type> {
1047 if let Type::Path(TypePath {
1048 qself: None,
1049 path:
1050 Path {
1051 leading_colon: None,
1052 segments,
1053 },
1054 }) = ty
1055 {
1056 if segments.len() == 1 {
1057 let PathSegment { ident, arguments } = &segments[0];
1058 if ident == "Option" {
1059 if let PathArguments::AngleBracketed(
1060 AngleBracketedGenericArguments { args, .. },
1061 ) = arguments
1062 {
1063 if args.len() == 1 {
1064 if let GenericArgument::Type(ty) = &args[0] {
1065 return Some(ty);
1066 }
1067 }
1068 }
1069 }
1070 }
1071 }
1072 None
1073}
1074
1075fn ident_path(ident: Ident) -> Path {
1076 let mut segments = Punctuated::new();
1077 segments.push_value(PathSegment {
1078 ident,
1079 arguments: PathArguments::None,
1080 });
1081 Path {
1082 leading_colon: None,
1083 segments,
1084 }
1085}
1086
1087fn remove_if<T, F>(vec: &mut Vec<T>, mut filter: F)
1088where
1089 F: FnMut(&mut T) -> bool,
1090{
1091 let mut i = 0;
1092 while i < vec.len() {
1093 if filter(&mut vec[i]) {
1094 vec.remove(i);
1095 } else {
1096 i += 1;
1097 }
1098 }
1099}