1#![deny(missing_docs)]
6
7use std::collections::{BTreeMap, BTreeSet};
8
9use conversions::SchemaCache;
10use log::info;
11use output::OutputSpace;
12use proc_macro2::TokenStream;
13use quote::{format_ident, quote, ToTokens};
14use schemars::schema::{Metadata, RootSchema, Schema};
15use thiserror::Error;
16use type_entry::{
17 StructPropertyState, TypeEntry, TypeEntryDetails, TypeEntryNative, TypeEntryNewtype,
18 WrappedValue,
19};
20
21use crate::util::{sanitize, Case};
22
23pub use crate::util::accept_as_ident;
24
25#[cfg(test)]
26mod test_util;
27
28mod conversions;
29mod convert;
30mod cycles;
31mod defaults;
32mod enums;
33mod merge;
34mod output;
35mod rust_extension;
36mod structs;
37mod type_entry;
38mod util;
39mod validate;
40mod value;
41
42#[allow(missing_docs)]
43#[derive(Error, Debug)]
44pub enum Error {
45 #[error("unexpected value type")]
46 BadValue(String, serde_json::Value),
47 #[error("invalid TypeId")]
48 InvalidTypeId,
49 #[error("value does not conform to the given schema")]
50 InvalidValue,
51 #[error("invalid schema for {}: {reason}", show_type_name(.type_name.as_deref()))]
52 InvalidSchema {
53 type_name: Option<String>,
54 reason: String,
55 },
56}
57
58impl Error {
59 fn invalid_value() -> Self {
60 Self::InvalidValue
61 }
62}
63
64#[allow(missing_docs)]
65pub type Result<T> = std::result::Result<T, Error>;
66
67fn show_type_name(type_name: Option<&str>) -> &str {
68 type_name.unwrap_or("<unknown type>")
69}
70
71#[derive(Debug)]
73pub struct Type<'a> {
74 type_space: &'a TypeSpace,
75 type_entry: &'a TypeEntry,
76}
77
78#[allow(missing_docs)]
79pub enum TypeDetails<'a> {
81 Enum(TypeEnum<'a>),
82 Struct(TypeStruct<'a>),
83 Newtype(TypeNewtype<'a>),
84
85 Option(TypeId),
86 Vec(TypeId),
87 Map(TypeId, TypeId),
88 Set(TypeId),
89 Box(TypeId),
90 Tuple(Box<dyn Iterator<Item = TypeId> + 'a>),
91 Array(TypeId, usize),
92 Builtin(&'a str),
93
94 Unit,
95 String,
96}
97
98pub struct TypeEnum<'a> {
100 details: &'a type_entry::TypeEntryEnum,
101}
102
103pub enum TypeEnumVariant<'a> {
105 Simple,
107 Tuple(Vec<TypeId>),
109 Struct(Vec<(&'a str, TypeId)>),
111}
112
113pub struct TypeEnumVariantInfo<'a> {
115 pub name: &'a str,
117 pub description: Option<&'a str>,
119 pub details: TypeEnumVariant<'a>,
121}
122
123pub struct TypeStruct<'a> {
125 details: &'a type_entry::TypeEntryStruct,
126}
127
128pub struct TypeStructPropInfo<'a> {
130 pub name: &'a str,
132 pub description: Option<&'a str>,
134 pub required: bool,
136 pub type_id: TypeId,
138}
139
140pub struct TypeNewtype<'a> {
142 details: &'a type_entry::TypeEntryNewtype,
143}
144
145#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone, Hash)]
147pub struct TypeId(u64);
148
149#[derive(Debug, Clone, PartialEq)]
150pub(crate) enum Name {
151 Required(String),
152 Suggested(String),
153 Unknown,
154}
155
156impl Name {
157 pub fn into_option(self) -> Option<String> {
158 match self {
159 Name::Required(s) | Name::Suggested(s) => Some(s),
160 Name::Unknown => None,
161 }
162 }
163
164 pub fn append(&self, s: &str) -> Self {
165 match self {
166 Name::Required(prefix) | Name::Suggested(prefix) => {
167 Self::Suggested(format!("{}_{}", prefix, s))
168 }
169 Name::Unknown => Name::Unknown,
170 }
171 }
172}
173
174#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
175pub(crate) enum RefKey {
176 Root,
177 Def(String),
178}
179
180#[derive(Debug)]
182pub struct TypeSpace {
183 next_id: u64,
184
185 definitions: BTreeMap<RefKey, Schema>,
190
191 id_to_entry: BTreeMap<TypeId, TypeEntry>,
192 type_to_id: BTreeMap<TypeEntryDetails, TypeId>,
193
194 name_to_id: BTreeMap<String, TypeId>,
195 ref_to_id: BTreeMap<RefKey, TypeId>,
196
197 uses_chrono: bool,
198 uses_uuid: bool,
199 uses_serde_json: bool,
200 uses_regress: bool,
201
202 settings: TypeSpaceSettings,
203
204 cache: SchemaCache,
205
206 defaults: BTreeSet<DefaultImpl>,
208}
209
210impl Default for TypeSpace {
211 fn default() -> Self {
212 Self {
213 next_id: 1,
214 definitions: Default::default(),
215 id_to_entry: Default::default(),
216 type_to_id: Default::default(),
217 name_to_id: Default::default(),
218 ref_to_id: Default::default(),
219 uses_chrono: Default::default(),
220 uses_uuid: Default::default(),
221 uses_serde_json: Default::default(),
222 uses_regress: Default::default(),
223 settings: Default::default(),
224 cache: Default::default(),
225 defaults: Default::default(),
226 }
227 }
228}
229
230#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
231pub(crate) enum DefaultImpl {
232 Boolean,
233 I64,
234 U64,
235 NZU64,
236}
237
238#[derive(Clone)]
240pub struct MapType(pub syn::Type);
241
242impl MapType {
243 pub fn new(s: &str) -> Self {
245 let map_type = syn::parse_str::<syn::Type>(s).expect("valid ident");
246 Self(map_type)
247 }
248}
249
250impl Default for MapType {
251 fn default() -> Self {
252 Self::new("::std::collections::HashMap")
253 }
254}
255
256impl std::fmt::Debug for MapType {
257 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258 write!(f, "MapType({})", self.0.to_token_stream())
259 }
260}
261
262impl std::fmt::Display for MapType {
263 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264 self.0.to_token_stream().fmt(f)
265 }
266}
267
268impl<'de> serde::Deserialize<'de> for MapType {
269 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
270 where
271 D: serde::Deserializer<'de>,
272 {
273 let s = <&str>::deserialize(deserializer)?;
274 Ok(Self::new(s))
275 }
276}
277
278impl From<String> for MapType {
279 fn from(s: String) -> Self {
280 Self::new(&s)
281 }
282}
283
284impl From<&str> for MapType {
285 fn from(s: &str) -> Self {
286 Self::new(s)
287 }
288}
289
290impl From<syn::Type> for MapType {
291 fn from(t: syn::Type) -> Self {
292 Self(t)
293 }
294}
295
296#[derive(Default, Debug, Clone)]
298pub struct TypeSpaceSettings {
299 type_mod: Option<String>,
300 extra_derives: Vec<String>,
301 extra_attrs: Vec<String>,
302 struct_builder: bool,
303
304 unknown_crates: UnknownPolicy,
305 crates: BTreeMap<String, CrateSpec>,
306 map_type: MapType,
307
308 patch: BTreeMap<String, TypeSpacePatch>,
309 replace: BTreeMap<String, TypeSpaceReplace>,
310 convert: Vec<TypeSpaceConversion>,
311}
312
313#[derive(Debug, Clone)]
314struct CrateSpec {
315 version: CrateVers,
316 rename: Option<String>,
317}
318
319#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Deserialize)]
322pub enum UnknownPolicy {
323 #[default]
325 Generate,
326 Allow,
332 Deny,
338}
339
340#[derive(Debug, Clone)]
343pub enum CrateVers {
344 Version(semver::Version),
346 Any,
348 Never,
350}
351
352impl CrateVers {
353 pub fn parse(s: &str) -> Option<Self> {
355 if s == "!" {
356 Some(Self::Never)
357 } else if s == "*" {
358 Some(Self::Any)
359 } else {
360 Some(Self::Version(semver::Version::parse(s).ok()?))
361 }
362 }
363}
364
365#[derive(Debug, Default, Clone)]
367pub struct TypeSpacePatch {
368 rename: Option<String>,
369 derives: Vec<String>,
370 attrs: Vec<String>,
371}
372
373#[derive(Debug, Default, Clone)]
375pub struct TypeSpaceReplace {
376 replace_type: String,
377 impls: Vec<TypeSpaceImpl>,
378}
379
380#[derive(Debug, Clone)]
383struct TypeSpaceConversion {
384 schema: schemars::schema::SchemaObject,
385 type_name: String,
386 impls: Vec<TypeSpaceImpl>,
387}
388
389#[allow(missing_docs)]
390#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
393#[non_exhaustive]
394pub enum TypeSpaceImpl {
395 FromStr,
396 FromStringIrrefutable,
397 Display,
398 Default,
399}
400
401impl std::str::FromStr for TypeSpaceImpl {
402 type Err = String;
403
404 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
405 match s {
406 "FromStr" => Ok(Self::FromStr),
407 "Display" => Ok(Self::Display),
408 "Default" => Ok(Self::Default),
409 _ => Err(format!("{} is not a valid trait specifier", s)),
410 }
411 }
412}
413
414impl TypeSpaceSettings {
415 pub fn with_type_mod<S: AsRef<str>>(&mut self, type_mod: S) -> &mut Self {
417 self.type_mod = Some(type_mod.as_ref().to_string());
418 self
419 }
420
421 pub fn with_derive(&mut self, derive: String) -> &mut Self {
423 if !self.extra_derives.contains(&derive) {
424 self.extra_derives.push(derive);
425 }
426 self
427 }
428
429 pub fn with_attr(&mut self, attr: String) -> &mut Self {
431 if !self.extra_attrs.contains(&attr) {
432 self.extra_attrs.push(attr);
433 }
434 self
435 }
436
437 pub fn with_struct_builder(&mut self, struct_builder: bool) -> &mut Self {
439 self.struct_builder = struct_builder;
440 self
441 }
442
443 pub fn with_replacement<TS: ToString, RS: ToString, I: Iterator<Item = TypeSpaceImpl>>(
447 &mut self,
448 type_name: TS,
449 replace_type: RS,
450 impls: I,
451 ) -> &mut Self {
452 self.replace.insert(
453 type_name.to_string(),
454 TypeSpaceReplace {
455 replace_type: replace_type.to_string(),
456 impls: impls.collect(),
457 },
458 );
459 self
460 }
461
462 pub fn with_patch<S: ToString>(
467 &mut self,
468 type_name: S,
469 type_patch: &TypeSpacePatch,
470 ) -> &mut Self {
471 self.patch.insert(type_name.to_string(), type_patch.clone());
472 self
473 }
474
475 pub fn with_conversion<S: ToString, I: Iterator<Item = TypeSpaceImpl>>(
501 &mut self,
502 schema: schemars::schema::SchemaObject,
503 type_name: S,
504 impls: I,
505 ) -> &mut Self {
506 self.convert.push(TypeSpaceConversion {
507 schema,
508 type_name: type_name.to_string(),
509 impls: impls.collect(),
510 });
511 self
512 }
513
514 pub fn with_unknown_crates(&mut self, policy: UnknownPolicy) -> &mut Self {
519 self.unknown_crates = policy;
520 self
521 }
522
523 pub fn with_crate<S1: ToString>(
531 &mut self,
532 crate_name: S1,
533 version: CrateVers,
534 rename: Option<&String>,
535 ) -> &mut Self {
536 self.crates.insert(
537 crate_name.to_string(),
538 CrateSpec {
539 version,
540 rename: rename.cloned(),
541 },
542 );
543 self
544 }
545
546 pub fn with_map_type<T: Into<MapType>>(&mut self, map_type: T) -> &mut Self {
561 self.map_type = map_type.into();
562 self
563 }
564}
565
566impl TypeSpacePatch {
567 pub fn with_rename<S: ToString>(&mut self, rename: S) -> &mut Self {
569 self.rename = Some(rename.to_string());
570 self
571 }
572
573 pub fn with_derive<S: ToString>(&mut self, derive: S) -> &mut Self {
575 self.derives.push(derive.to_string());
576 self
577 }
578
579 pub fn with_attr<S: ToString>(&mut self, attr: S) -> &mut Self {
581 self.attrs.push(attr.to_string());
582 self
583 }
584}
585
586impl TypeSpace {
587 pub fn new(settings: &TypeSpaceSettings) -> Self {
589 let mut cache = SchemaCache::default();
590
591 settings.convert.iter().for_each(
592 |TypeSpaceConversion {
593 schema,
594 type_name,
595 impls,
596 }| {
597 cache.insert(schema, type_name, impls);
598 },
599 );
600
601 Self {
602 settings: settings.clone(),
603 cache,
604 ..Default::default()
605 }
606 }
607
608 pub fn add_ref_types<I, S>(&mut self, type_defs: I) -> Result<()>
618 where
619 I: IntoIterator<Item = (S, Schema)>,
620 S: AsRef<str>,
621 {
622 self.add_ref_types_impl(
623 type_defs
624 .into_iter()
625 .map(|(key, schema)| (RefKey::Def(key.as_ref().to_string()), schema)),
626 )
627 }
628
629 fn add_ref_types_impl<I>(&mut self, type_defs: I) -> Result<()>
630 where
631 I: IntoIterator<Item = (RefKey, Schema)>,
632 {
633 let definitions = type_defs.into_iter().collect::<Vec<_>>();
635
636 let base_id = self.next_id;
639 let def_len = definitions.len() as u64;
640 self.next_id += def_len;
641
642 for (index, (ref_name, schema)) in definitions.iter().enumerate() {
643 self.ref_to_id
644 .insert(ref_name.clone(), TypeId(base_id + index as u64));
645 self.definitions.insert(ref_name.clone(), schema.clone());
646 }
647
648 for (index, (ref_name, schema)) in definitions.into_iter().enumerate() {
653 info!(
654 "converting type: {:?} with schema {}",
655 ref_name,
656 serde_json::to_string(&schema).unwrap()
657 );
658
659 let type_id = TypeId(base_id + index as u64);
662
663 let maybe_replace = match &ref_name {
664 RefKey::Root => None,
665 RefKey::Def(def_name) => {
666 let check_name = sanitize(def_name, Case::Pascal);
667 self.settings.replace.get(&check_name)
668 }
669 };
670
671 match maybe_replace {
672 None => {
673 let type_name = if let RefKey::Def(name) = ref_name {
674 Name::Required(name.clone())
675 } else {
676 Name::Unknown
677 };
678 self.convert_ref_type(type_name, schema, type_id)?
679 }
680
681 Some(replace_type) => {
682 let type_entry = TypeEntry::new_native(
683 replace_type.replace_type.clone(),
684 &replace_type.impls.clone(),
685 );
686 self.id_to_entry.insert(type_id, type_entry);
687 }
688 }
689 }
690
691 self.break_cycles(base_id..base_id + def_len);
694
695 for index in base_id..self.next_id {
697 let type_id = TypeId(index);
698 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
699 type_entry.finalize(self)?;
700 self.id_to_entry.insert(type_id, type_entry);
701 }
702
703 Ok(())
704 }
705
706 fn convert_ref_type(&mut self, type_name: Name, schema: Schema, type_id: TypeId) -> Result<()> {
707 let (mut type_entry, metadata) = self.convert_schema(type_name.clone(), &schema)?;
708 let default = metadata
709 .as_ref()
710 .and_then(|m| m.default.as_ref())
711 .cloned()
712 .map(WrappedValue::new);
713 let type_entry = match &mut type_entry.details {
714 TypeEntryDetails::Enum(details) => {
716 details.default = default;
717 type_entry
718 }
719 TypeEntryDetails::Struct(details) => {
720 details.default = default;
721 type_entry
722 }
723 TypeEntryDetails::Newtype(details) => {
724 details.default = default;
725 type_entry
726 }
727
728 TypeEntryDetails::Reference(type_id) => TypeEntryNewtype::from_metadata(
733 self,
734 type_name,
735 metadata,
736 type_id.clone(),
737 schema.clone(),
738 ),
739
740 TypeEntryDetails::Native(native) if native.name_match(&type_name) => type_entry,
741
742 _ => {
745 info!(
746 "type alias {:?} {}\n{:?}",
747 type_name,
748 serde_json::to_string_pretty(&schema).unwrap(),
749 metadata
750 );
751 let subtype_id = self.assign_type(type_entry);
752 TypeEntryNewtype::from_metadata(
753 self,
754 type_name,
755 metadata,
756 subtype_id,
757 schema.clone(),
758 )
759 }
760 };
761 if let Some(entry_name) = type_entry.name() {
763 self.name_to_id.insert(entry_name.clone(), type_id.clone());
764 }
765 self.id_to_entry.insert(type_id, type_entry);
766 Ok(())
767 }
768
769 pub fn add_type(&mut self, schema: &Schema) -> Result<TypeId> {
772 self.add_type_with_name(schema, None)
773 }
774
775 pub fn add_type_with_name(
778 &mut self,
779 schema: &Schema,
780 name_hint: Option<String>,
781 ) -> Result<TypeId> {
782 let base_id = self.next_id;
783
784 let name = match name_hint {
785 Some(s) => Name::Suggested(s),
786 None => Name::Unknown,
787 };
788 let (type_id, _) = self.id_for_schema(name, schema)?;
789
790 for index in base_id..self.next_id {
792 let type_id = TypeId(index);
793 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
794 type_entry.finalize(self)?;
795 self.id_to_entry.insert(type_id, type_entry);
796 }
797
798 Ok(type_id)
799 }
800
801 pub fn add_root_schema(&mut self, schema: RootSchema) -> Result<Option<TypeId>> {
805 let RootSchema {
806 meta_schema: _,
807 schema,
808 definitions,
809 } = schema;
810
811 let mut defs = definitions
812 .into_iter()
813 .map(|(key, schema)| (RefKey::Def(key), schema))
814 .collect::<Vec<_>>();
815
816 let root_type = schema
818 .metadata
819 .as_ref()
820 .and_then(|m| m.title.as_ref())
821 .is_some();
822
823 if root_type {
824 defs.push((RefKey::Root, schema.into()));
825 }
826
827 self.add_ref_types_impl(defs)?;
828
829 if root_type {
830 Ok(self.ref_to_id.get(&RefKey::Root).cloned())
831 } else {
832 Ok(None)
833 }
834 }
835
836 pub fn get_type(&self, type_id: &TypeId) -> Result<Type<'_>> {
838 let type_entry = self.id_to_entry.get(type_id).ok_or(Error::InvalidTypeId)?;
839 Ok(Type {
840 type_space: self,
841 type_entry,
842 })
843 }
844
845 pub fn uses_chrono(&self) -> bool {
847 self.uses_chrono
848 }
849
850 pub fn uses_regress(&self) -> bool {
852 self.uses_regress
853 }
854
855 pub fn uses_serde_json(&self) -> bool {
857 self.uses_serde_json
858 }
859
860 pub fn uses_uuid(&self) -> bool {
862 self.uses_uuid
863 }
864
865 pub fn iter_types(&self) -> impl Iterator<Item = Type<'_>> {
868 self.id_to_entry.values().map(move |type_entry| Type {
869 type_space: self,
870 type_entry,
871 })
872 }
873
874 pub fn to_stream(&self) -> TokenStream {
876 let mut output = OutputSpace::default();
877
878 output.add_item(
881 output::OutputSpaceMod::Error,
882 "",
883 quote! {
884 pub struct ConversionError(::std::borrow::Cow<'static, str>);
886
887 impl ::std::error::Error for ConversionError {}
888 impl ::std::fmt::Display for ConversionError {
889 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
890 -> Result<(), ::std::fmt::Error>
891 {
892 ::std::fmt::Display::fmt(&self.0, f)
893 }
894 }
895
896 impl ::std::fmt::Debug for ConversionError {
897 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
898 -> Result<(), ::std::fmt::Error>
899 {
900 ::std::fmt::Debug::fmt(&self.0, f)
901 }
902 }
903 impl From<&'static str> for ConversionError {
904 fn from(value: &'static str) -> Self {
905 Self(value.into())
906 }
907 }
908 impl From<String> for ConversionError {
909 fn from(value: String) -> Self {
910 Self(value.into())
911 }
912 }
913 },
914 );
915
916 self.id_to_entry
918 .values()
919 .for_each(|type_entry| type_entry.output(self, &mut output));
920
921 self.defaults
923 .iter()
924 .for_each(|x| output.add_item(output::OutputSpaceMod::Defaults, "", x.into()));
925
926 output.into_stream()
927 }
928
929 fn assign(&mut self) -> TypeId {
931 let id = TypeId(self.next_id);
932 self.next_id += 1;
933 id
934 }
935
936 fn assign_type(&mut self, ty: TypeEntry) -> TypeId {
941 if let TypeEntryDetails::Reference(type_id) = ty.details {
942 type_id
943 } else if let Some(name) = ty.name() {
944 if let Some(type_id) = self.name_to_id.get(name) {
954 type_id.clone()
960 } else {
961 let type_id = self.assign();
962 self.name_to_id.insert(name.clone(), type_id.clone());
963 self.id_to_entry.insert(type_id.clone(), ty);
964 type_id
965 }
966 } else if let Some(type_id) = self.type_to_id.get(&ty.details) {
967 type_id.clone()
968 } else {
969 let type_id = self.assign();
970 self.type_to_id.insert(ty.details.clone(), type_id.clone());
971 self.id_to_entry.insert(type_id.clone(), ty);
972 type_id
973 }
974 }
975
976 fn id_for_schema<'a>(
981 &mut self,
982 type_name: Name,
983 schema: &'a Schema,
984 ) -> Result<(TypeId, &'a Option<Box<Metadata>>)> {
985 let (mut type_entry, metadata) = self.convert_schema(type_name, schema)?;
986 if let Some(metadata) = metadata {
987 let default = metadata.default.clone().map(WrappedValue::new);
988 match &mut type_entry.details {
989 TypeEntryDetails::Enum(details) => {
990 details.default = default;
991 }
992 TypeEntryDetails::Struct(details) => {
993 details.default = default;
994 }
995 TypeEntryDetails::Newtype(details) => {
996 details.default = default;
997 }
998 _ => (),
999 }
1000 }
1001 let type_id = self.assign_type(type_entry);
1002 Ok((type_id, metadata))
1003 }
1004
1005 fn id_to_option(&mut self, id: &TypeId) -> TypeId {
1007 self.assign_type(TypeEntryDetails::Option(id.clone()).into())
1008 }
1009
1010 fn type_to_option(&mut self, ty: TypeEntry) -> TypeEntry {
1012 TypeEntryDetails::Option(self.assign_type(ty)).into()
1013 }
1014
1015 fn id_to_box(&mut self, id: &TypeId) -> TypeId {
1017 self.assign_type(TypeEntryDetails::Box(id.clone()).into())
1018 }
1019}
1020
1021impl ToTokens for TypeSpace {
1022 fn to_tokens(&self, tokens: &mut TokenStream) {
1023 tokens.extend(self.to_stream())
1024 }
1025}
1026
1027impl Type<'_> {
1028 pub fn name(&self) -> String {
1030 let Type {
1031 type_space,
1032 type_entry,
1033 } = self;
1034 type_entry.type_name(type_space)
1035 }
1036
1037 pub fn ident(&self) -> TokenStream {
1040 let Type {
1041 type_space,
1042 type_entry,
1043 } = self;
1044 type_entry.type_ident(type_space, &type_space.settings.type_mod)
1045 }
1046
1047 pub fn parameter_ident(&self) -> TokenStream {
1051 let Type {
1052 type_space,
1053 type_entry,
1054 } = self;
1055 type_entry.type_parameter_ident(type_space, None)
1056 }
1057
1058 pub fn parameter_ident_with_lifetime(&self, lifetime: &str) -> TokenStream {
1063 let Type {
1064 type_space,
1065 type_entry,
1066 } = self;
1067 type_entry.type_parameter_ident(type_space, Some(lifetime))
1068 }
1069
1070 pub fn describe(&self) -> String {
1072 self.type_entry.describe()
1073 }
1074
1075 pub fn details(&self) -> TypeDetails<'_> {
1077 match &self.type_entry.details {
1078 TypeEntryDetails::Enum(details) => TypeDetails::Enum(TypeEnum { details }),
1080 TypeEntryDetails::Struct(details) => TypeDetails::Struct(TypeStruct { details }),
1081 TypeEntryDetails::Newtype(details) => TypeDetails::Newtype(TypeNewtype { details }),
1082
1083 TypeEntryDetails::Option(type_id) => TypeDetails::Option(type_id.clone()),
1085 TypeEntryDetails::Vec(type_id) => TypeDetails::Vec(type_id.clone()),
1086 TypeEntryDetails::Map(key_id, value_id) => {
1087 TypeDetails::Map(key_id.clone(), value_id.clone())
1088 }
1089 TypeEntryDetails::Set(type_id) => TypeDetails::Set(type_id.clone()),
1090 TypeEntryDetails::Box(type_id) => TypeDetails::Box(type_id.clone()),
1091 TypeEntryDetails::Tuple(types) => TypeDetails::Tuple(Box::new(types.iter().cloned())),
1092 TypeEntryDetails::Array(type_id, length) => {
1093 TypeDetails::Array(type_id.clone(), *length)
1094 }
1095
1096 TypeEntryDetails::Unit => TypeDetails::Unit,
1098 TypeEntryDetails::Native(TypeEntryNative {
1099 type_name: name, ..
1100 })
1101 | TypeEntryDetails::Integer(name)
1102 | TypeEntryDetails::Float(name) => TypeDetails::Builtin(name.as_str()),
1103 TypeEntryDetails::Boolean => TypeDetails::Builtin("bool"),
1104 TypeEntryDetails::String => TypeDetails::String,
1105 TypeEntryDetails::JsonValue => TypeDetails::Builtin("::serde_json::Value"),
1106
1107 TypeEntryDetails::Reference(_) => unreachable!(),
1109 }
1110 }
1111
1112 pub fn has_impl(&self, impl_name: TypeSpaceImpl) -> bool {
1114 let Type {
1115 type_space,
1116 type_entry,
1117 } = self;
1118 type_entry.has_impl(type_space, impl_name)
1119 }
1120
1121 pub fn builder(&self) -> Option<TokenStream> {
1123 let Type {
1124 type_space,
1125 type_entry,
1126 } = self;
1127
1128 if !type_space.settings.struct_builder {
1129 return None;
1130 }
1131
1132 match &type_entry.details {
1133 TypeEntryDetails::Struct(type_entry::TypeEntryStruct { name, .. }) => {
1134 match &type_space.settings.type_mod {
1135 Some(type_mod) => {
1136 let type_mod = format_ident!("{}", type_mod);
1137 let type_name = format_ident!("{}", name);
1138 Some(quote! { #type_mod :: builder :: #type_name })
1139 }
1140 None => {
1141 let type_name = format_ident!("{}", name);
1142 Some(quote! { builder :: #type_name })
1143 }
1144 }
1145 }
1146 _ => None,
1147 }
1148 }
1149}
1150
1151impl<'a> TypeEnum<'a> {
1152 pub fn variants(&'a self) -> impl Iterator<Item = (&'a str, TypeEnumVariant<'a>)> {
1154 self.variants_info().map(|info| (info.name, info.details))
1155 }
1156
1157 pub fn variants_info(&'a self) -> impl Iterator<Item = TypeEnumVariantInfo<'a>> {
1159 self.details.variants.iter().map(move |variant| {
1160 let details = match &variant.details {
1161 type_entry::VariantDetails::Simple => TypeEnumVariant::Simple,
1162 type_entry::VariantDetails::Item(type_id) => {
1165 TypeEnumVariant::Tuple(vec![type_id.clone()])
1166 }
1167 type_entry::VariantDetails::Tuple(types) => TypeEnumVariant::Tuple(types.clone()),
1168 type_entry::VariantDetails::Struct(properties) => TypeEnumVariant::Struct(
1169 properties
1170 .iter()
1171 .map(|prop| (prop.name.as_str(), prop.type_id.clone()))
1172 .collect(),
1173 ),
1174 };
1175 TypeEnumVariantInfo {
1176 name: variant.ident_name.as_ref().unwrap(),
1177 description: variant.description.as_deref(),
1178 details,
1179 }
1180 })
1181 }
1182}
1183
1184impl<'a> TypeStruct<'a> {
1185 pub fn properties(&'a self) -> impl Iterator<Item = (&'a str, TypeId)> {
1187 self.details
1188 .properties
1189 .iter()
1190 .map(move |prop| (prop.name.as_str(), prop.type_id.clone()))
1191 }
1192
1193 pub fn properties_info(&'a self) -> impl Iterator<Item = TypeStructPropInfo<'a>> {
1195 self.details
1196 .properties
1197 .iter()
1198 .map(move |prop| TypeStructPropInfo {
1199 name: prop.name.as_str(),
1200 description: prop.description.as_deref(),
1201 required: matches!(&prop.state, StructPropertyState::Required),
1202 type_id: prop.type_id.clone(),
1203 })
1204 }
1205}
1206
1207impl TypeNewtype<'_> {
1208 pub fn inner(&self) -> TypeId {
1210 self.details.type_id.clone()
1211 }
1212}
1213
1214#[cfg(test)]
1215mod tests {
1216 use schema::Schema;
1217 use schemars::{schema_for, JsonSchema};
1218 use serde::Serialize;
1219 use serde_json::json;
1220 use std::collections::HashSet;
1221
1222 use crate::{
1223 output::OutputSpace,
1224 test_util::validate_output,
1225 type_entry::{TypeEntryEnum, VariantDetails},
1226 Name, TypeEntryDetails, TypeSpace, TypeSpaceSettings,
1227 };
1228
1229 #[allow(dead_code)]
1230 #[derive(Serialize, JsonSchema)]
1231 struct Blah {
1232 blah: String,
1233 }
1234
1235 #[allow(dead_code)]
1236 #[derive(Serialize, JsonSchema)]
1237 #[serde(rename_all = "camelCase")]
1238 enum E {
1241 A,
1243 B,
1245 C(Blah),
1248 D {
1250 dd: String,
1252 },
1253 }
1261
1262 #[allow(dead_code)]
1263 #[derive(JsonSchema)]
1264 #[serde(rename_all = "camelCase")]
1265 struct Foo {
1266 #[serde(default)]
1268 bar: Option<String>,
1269 baz_baz: i32,
1270 e: E,
1272 }
1273
1274 #[test]
1275 fn test_simple() {
1276 let schema = schema_for!(Foo);
1277 println!("{:#?}", schema);
1278 let mut type_space = TypeSpace::default();
1279 type_space.add_ref_types(schema.definitions).unwrap();
1280 let (ty, _) = type_space
1281 .convert_schema_object(
1282 Name::Unknown,
1283 &schemars::schema::Schema::Object(schema.schema.clone()),
1284 &schema.schema,
1285 )
1286 .unwrap();
1287
1288 println!("{:#?}", ty);
1289
1290 let mut output = OutputSpace::default();
1291 ty.output(&type_space, &mut output);
1292 println!("{}", output.into_stream());
1293
1294 for ty in type_space.id_to_entry.values() {
1295 println!("{:#?}", ty);
1296 let mut output = OutputSpace::default();
1297 ty.output(&type_space, &mut output);
1298 println!("{}", output.into_stream());
1299 }
1300 }
1301
1302 #[test]
1303 fn test_external_references() {
1304 let schema = json!({
1305 "$schema": "http://json-schema.org/draft-04/schema#",
1306 "definitions": {
1307 "somename": {
1308 "$ref": "#/definitions/someothername",
1309 "required": [ "someproperty" ]
1310 },
1311 "someothername": {
1312 "type": "object",
1313 "properties": {
1314 "someproperty": {
1315 "type": "string"
1316 }
1317 }
1318 }
1319 }
1320 });
1321 let schema = serde_json::from_value(schema).unwrap();
1322 println!("{:#?}", schema);
1323 let settings = TypeSpaceSettings::default();
1324 let mut type_space = TypeSpace::new(&settings);
1325 type_space.add_root_schema(schema).unwrap();
1326 let tokens = type_space.to_stream().to_string();
1327 println!("{}", tokens);
1328 assert!(tokens
1329 .contains(" pub struct Somename { pub someproperty : :: std :: string :: String , }"))
1330 }
1331
1332 #[test]
1333 fn test_convert_enum_string() {
1334 #[allow(dead_code)]
1335 #[derive(JsonSchema)]
1336 #[serde(rename_all = "camelCase")]
1337 enum SimpleEnum {
1338 DotCom,
1339 Grizz,
1340 Kenneth,
1341 }
1342
1343 let schema = schema_for!(SimpleEnum);
1344 println!("{:#?}", schema);
1345
1346 let mut type_space = TypeSpace::default();
1347 type_space.add_ref_types(schema.definitions).unwrap();
1348 let (ty, _) = type_space
1349 .convert_schema_object(
1350 Name::Unknown,
1351 &schemars::schema::Schema::Object(schema.schema.clone()),
1352 &schema.schema,
1353 )
1354 .unwrap();
1355
1356 match ty.details {
1357 TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) => {
1358 for variant in &variants {
1359 assert_eq!(variant.details, VariantDetails::Simple);
1360 }
1361 let var_names = variants
1362 .iter()
1363 .map(|variant| variant.ident_name.as_ref().unwrap().clone())
1364 .collect::<HashSet<_>>();
1365 assert_eq!(
1366 var_names,
1367 ["DotCom", "Grizz", "Kenneth",]
1368 .iter()
1369 .map(ToString::to_string)
1370 .collect::<HashSet<_>>()
1371 );
1372 }
1373 _ => {
1374 let mut output = OutputSpace::default();
1375 ty.output(&type_space, &mut output);
1376 println!("{}", output.into_stream());
1377 panic!();
1378 }
1379 }
1380 }
1381
1382 #[test]
1383 fn test_string_enum_with_null() {
1384 let original_schema = json!({ "$ref": "xxx"});
1385 let enum_values = vec![
1386 json!("Shadrach"),
1387 json!("Meshach"),
1388 json!("Abednego"),
1389 json!(null),
1390 ];
1391
1392 let mut type_space = TypeSpace::default();
1393 let (te, _) = type_space
1394 .convert_enum_string(
1395 Name::Required("OnTheGo".to_string()),
1396 &serde_json::from_value(original_schema).unwrap(),
1397 &None,
1398 &enum_values,
1399 None,
1400 )
1401 .unwrap();
1402
1403 if let TypeEntryDetails::Option(id) = &te.details {
1404 let ote = type_space.id_to_entry.get(id).unwrap();
1405 if let TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) = &ote.details {
1406 let variants = variants
1407 .iter()
1408 .map(|v| match v.details {
1409 VariantDetails::Simple => v.ident_name.as_ref().unwrap().clone(),
1410 _ => panic!("unexpected variant type"),
1411 })
1412 .collect::<HashSet<_>>();
1413
1414 assert_eq!(
1415 variants,
1416 enum_values
1417 .iter()
1418 .flat_map(|j| j.as_str().map(ToString::to_string))
1419 .collect::<HashSet<_>>()
1420 );
1421 } else {
1422 panic!("not the sub-type we expected {:#?}", te)
1423 }
1424 } else {
1425 panic!("not the type we expected {:#?}", te)
1426 }
1427 }
1428
1429 #[test]
1430 fn test_alias() {
1431 #[allow(dead_code)]
1432 #[derive(JsonSchema, Schema)]
1433 struct Stuff(Vec<String>);
1434
1435 #[allow(dead_code)]
1436 #[derive(JsonSchema, Schema)]
1437 struct Things {
1438 a: String,
1439 b: Stuff,
1440 }
1441
1442 validate_output::<Things>();
1443 }
1444
1445 #[test]
1446 fn test_builder_name() {
1447 #[allow(dead_code)]
1448 #[derive(JsonSchema)]
1449 struct TestStruct {
1450 x: u32,
1451 }
1452
1453 let mut type_space = TypeSpace::default();
1454 let schema = schema_for!(TestStruct);
1455 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1456 let ty = type_space.get_type(&type_id).unwrap();
1457
1458 assert!(ty.builder().is_none());
1459
1460 let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_struct_builder(true));
1461 let schema = schema_for!(TestStruct);
1462 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1463 let ty = type_space.get_type(&type_id).unwrap();
1464
1465 assert_eq!(
1466 ty.builder().map(|ts| ts.to_string()),
1467 Some("builder :: TestStruct".to_string())
1468 );
1469
1470 let mut type_space = TypeSpace::new(
1471 TypeSpaceSettings::default()
1472 .with_type_mod("types")
1473 .with_struct_builder(true),
1474 );
1475 let schema = schema_for!(TestStruct);
1476 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1477 let ty = type_space.get_type(&type_id).unwrap();
1478
1479 assert_eq!(
1480 ty.builder().map(|ts| ts.to_string()),
1481 Some("types :: builder :: TestStruct".to_string())
1482 );
1483
1484 #[allow(dead_code)]
1485 #[derive(JsonSchema)]
1486 enum TestEnum {
1487 X,
1488 Y,
1489 }
1490 let mut type_space = TypeSpace::new(
1491 TypeSpaceSettings::default()
1492 .with_type_mod("types")
1493 .with_struct_builder(true),
1494 );
1495 let schema = schema_for!(TestEnum);
1496 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1497 let ty = type_space.get_type(&type_id).unwrap();
1498 assert!(ty.builder().is_none());
1499 }
1500}