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
23#[cfg(test)]
24mod test_util;
25
26mod conversions;
27mod convert;
28mod cycles;
29mod defaults;
30mod enums;
31mod merge;
32mod output;
33mod rust_extension;
34mod structs;
35mod type_entry;
36mod util;
37mod validate;
38mod value;
39
40#[allow(missing_docs)]
41#[derive(Error, Debug)]
42pub enum Error {
43 #[error("unexpected value type")]
44 BadValue(String, serde_json::Value),
45 #[error("invalid TypeId")]
46 InvalidTypeId,
47 #[error("value does not conform to the given schema")]
48 InvalidValue,
49 #[error("invalid schema for {}: {reason}", show_type_name(.type_name.as_deref()))]
50 InvalidSchema {
51 type_name: Option<String>,
52 reason: String,
53 },
54}
55
56impl Error {
57 fn invalid_value() -> Self {
58 Self::InvalidValue
59 }
60}
61
62#[allow(missing_docs)]
63pub type Result<T> = std::result::Result<T, Error>;
64
65fn show_type_name(type_name: Option<&str>) -> &str {
66 type_name.unwrap_or("<unknown type>")
67}
68
69#[derive(Debug)]
71pub struct Type<'a> {
72 type_space: &'a TypeSpace,
73 type_entry: &'a TypeEntry,
74}
75
76#[allow(missing_docs)]
77pub enum TypeDetails<'a> {
79 Enum(TypeEnum<'a>),
80 Struct(TypeStruct<'a>),
81 Newtype(TypeNewtype<'a>),
82
83 Option(TypeId),
84 Vec(TypeId),
85 Map(TypeId, TypeId),
86 Set(TypeId),
87 Box(TypeId),
88 Tuple(Box<dyn Iterator<Item = TypeId> + 'a>),
89 Array(TypeId, usize),
90 Builtin(&'a str),
91
92 Unit,
93 String,
94}
95
96pub struct TypeEnum<'a> {
98 details: &'a type_entry::TypeEntryEnum,
99}
100
101pub enum TypeEnumVariant<'a> {
103 Simple,
105 Tuple(Vec<TypeId>),
107 Struct(Vec<(&'a str, TypeId)>),
109}
110
111pub struct TypeEnumVariantInfo<'a> {
113 pub name: &'a str,
115 pub description: Option<&'a str>,
117 pub details: TypeEnumVariant<'a>,
119}
120
121pub struct TypeStruct<'a> {
123 details: &'a type_entry::TypeEntryStruct,
124}
125
126pub struct TypeStructPropInfo<'a> {
128 pub name: &'a str,
130 pub description: Option<&'a str>,
132 pub required: bool,
134 pub type_id: TypeId,
136}
137
138pub struct TypeNewtype<'a> {
140 details: &'a type_entry::TypeEntryNewtype,
141}
142
143#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone, Hash)]
145pub struct TypeId(u64);
146
147#[derive(Debug, Clone, PartialEq)]
148pub(crate) enum Name {
149 Required(String),
150 Suggested(String),
151 Unknown,
152}
153
154impl Name {
155 pub fn into_option(self) -> Option<String> {
156 match self {
157 Name::Required(s) | Name::Suggested(s) => Some(s),
158 Name::Unknown => None,
159 }
160 }
161
162 pub fn append(&self, s: &str) -> Self {
163 match self {
164 Name::Required(prefix) | Name::Suggested(prefix) => {
165 Self::Suggested(format!("{}_{}", prefix, s))
166 }
167 Name::Unknown => Name::Unknown,
168 }
169 }
170}
171
172#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
173pub(crate) enum RefKey {
174 Root,
175 Def(String),
176}
177
178#[derive(Debug)]
180pub struct TypeSpace {
181 next_id: u64,
182
183 definitions: BTreeMap<RefKey, Schema>,
188
189 id_to_entry: BTreeMap<TypeId, TypeEntry>,
190 type_to_id: BTreeMap<TypeEntryDetails, TypeId>,
191
192 name_to_id: BTreeMap<String, TypeId>,
193 ref_to_id: BTreeMap<RefKey, TypeId>,
194
195 uses_chrono: bool,
196 uses_uuid: bool,
197 uses_serde_json: bool,
198 uses_regress: bool,
199
200 settings: TypeSpaceSettings,
201
202 cache: SchemaCache,
203
204 defaults: BTreeSet<DefaultImpl>,
206}
207
208impl Default for TypeSpace {
209 fn default() -> Self {
210 Self {
211 next_id: 1,
212 definitions: Default::default(),
213 id_to_entry: Default::default(),
214 type_to_id: Default::default(),
215 name_to_id: Default::default(),
216 ref_to_id: Default::default(),
217 uses_chrono: Default::default(),
218 uses_uuid: Default::default(),
219 uses_serde_json: Default::default(),
220 uses_regress: Default::default(),
221 settings: Default::default(),
222 cache: Default::default(),
223 defaults: Default::default(),
224 }
225 }
226}
227
228#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
229pub(crate) enum DefaultImpl {
230 Boolean,
231 I64,
232 U64,
233 NZU64,
234}
235
236#[derive(Clone)]
238pub struct MapType(pub syn::Type);
239
240impl MapType {
241 pub fn new(s: &str) -> Self {
243 let map_type = syn::parse_str::<syn::Type>(s).expect("valid ident");
244 Self(map_type)
245 }
246}
247
248impl Default for MapType {
249 fn default() -> Self {
250 Self::new("::std::collections::HashMap")
251 }
252}
253
254impl std::fmt::Debug for MapType {
255 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256 write!(f, "MapType({})", self.0.to_token_stream())
257 }
258}
259
260impl std::fmt::Display for MapType {
261 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262 self.0.to_token_stream().fmt(f)
263 }
264}
265
266impl<'de> serde::Deserialize<'de> for MapType {
267 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
268 where
269 D: serde::Deserializer<'de>,
270 {
271 let s = <&str>::deserialize(deserializer)?;
272 Ok(Self::new(s))
273 }
274}
275
276impl From<String> for MapType {
277 fn from(s: String) -> Self {
278 Self::new(&s)
279 }
280}
281
282impl From<&str> for MapType {
283 fn from(s: &str) -> Self {
284 Self::new(s)
285 }
286}
287
288impl From<syn::Type> for MapType {
289 fn from(t: syn::Type) -> Self {
290 Self(t)
291 }
292}
293
294#[derive(Default, Debug, Clone)]
296pub struct TypeSpaceSettings {
297 type_mod: Option<String>,
298 extra_derives: Vec<String>,
299 struct_builder: bool,
300
301 unknown_crates: UnknownPolicy,
302 crates: BTreeMap<String, CrateSpec>,
303 map_type: MapType,
304
305 patch: BTreeMap<String, TypeSpacePatch>,
306 replace: BTreeMap<String, TypeSpaceReplace>,
307 convert: Vec<TypeSpaceConversion>,
308}
309
310#[derive(Debug, Clone)]
311struct CrateSpec {
312 version: CrateVers,
313 rename: Option<String>,
314}
315
316#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Deserialize)]
319pub enum UnknownPolicy {
320 #[default]
322 Generate,
323 Allow,
329 Deny,
335}
336
337#[derive(Debug, Clone)]
340pub enum CrateVers {
341 Version(semver::Version),
343 Any,
345 Never,
347}
348
349impl CrateVers {
350 pub fn parse(s: &str) -> Option<Self> {
352 if s == "!" {
353 Some(Self::Never)
354 } else if s == "*" {
355 Some(Self::Any)
356 } else {
357 Some(Self::Version(semver::Version::parse(s).ok()?))
358 }
359 }
360}
361
362#[derive(Debug, Default, Clone)]
364pub struct TypeSpacePatch {
365 rename: Option<String>,
366 derives: Vec<String>,
367}
368
369#[derive(Debug, Default, Clone)]
371pub struct TypeSpaceReplace {
372 replace_type: String,
373 impls: Vec<TypeSpaceImpl>,
374}
375
376#[derive(Debug, Clone)]
379struct TypeSpaceConversion {
380 schema: schemars::schema::SchemaObject,
381 type_name: String,
382 impls: Vec<TypeSpaceImpl>,
383}
384
385#[allow(missing_docs)]
386#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
389#[non_exhaustive]
390pub enum TypeSpaceImpl {
391 FromStr,
392 FromStringIrrefutable,
393 Display,
394 Default,
395}
396
397impl std::str::FromStr for TypeSpaceImpl {
398 type Err = String;
399
400 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
401 match s {
402 "FromStr" => Ok(Self::FromStr),
403 "Display" => Ok(Self::Display),
404 "Default" => Ok(Self::Default),
405 _ => Err(format!("{} is not a valid trait specifier", s)),
406 }
407 }
408}
409
410impl TypeSpaceSettings {
411 pub fn with_type_mod<S: AsRef<str>>(&mut self, type_mod: S) -> &mut Self {
413 self.type_mod = Some(type_mod.as_ref().to_string());
414 self
415 }
416
417 pub fn with_derive(&mut self, derive: String) -> &mut Self {
419 if !self.extra_derives.contains(&derive) {
420 self.extra_derives.push(derive);
421 }
422 self
423 }
424
425 pub fn with_struct_builder(&mut self, struct_builder: bool) -> &mut Self {
427 self.struct_builder = struct_builder;
428 self
429 }
430
431 pub fn with_replacement<TS: ToString, RS: ToString, I: Iterator<Item = TypeSpaceImpl>>(
435 &mut self,
436 type_name: TS,
437 replace_type: RS,
438 impls: I,
439 ) -> &mut Self {
440 self.replace.insert(
441 type_name.to_string(),
442 TypeSpaceReplace {
443 replace_type: replace_type.to_string(),
444 impls: impls.collect(),
445 },
446 );
447 self
448 }
449
450 pub fn with_patch<S: ToString>(
455 &mut self,
456 type_name: S,
457 type_patch: &TypeSpacePatch,
458 ) -> &mut Self {
459 self.patch.insert(type_name.to_string(), type_patch.clone());
460 self
461 }
462
463 pub fn with_conversion<S: ToString, I: Iterator<Item = TypeSpaceImpl>>(
489 &mut self,
490 schema: schemars::schema::SchemaObject,
491 type_name: S,
492 impls: I,
493 ) -> &mut Self {
494 self.convert.push(TypeSpaceConversion {
495 schema,
496 type_name: type_name.to_string(),
497 impls: impls.collect(),
498 });
499 self
500 }
501
502 pub fn with_unknown_crates(&mut self, policy: UnknownPolicy) -> &mut Self {
507 self.unknown_crates = policy;
508 self
509 }
510
511 pub fn with_crate<S1: ToString>(
519 &mut self,
520 crate_name: S1,
521 version: CrateVers,
522 rename: Option<&String>,
523 ) -> &mut Self {
524 self.crates.insert(
525 crate_name.to_string(),
526 CrateSpec {
527 version,
528 rename: rename.cloned(),
529 },
530 );
531 self
532 }
533
534 pub fn with_map_type<T: Into<MapType>>(&mut self, map_type: T) -> &mut Self {
549 self.map_type = map_type.into();
550 self
551 }
552}
553
554impl TypeSpacePatch {
555 pub fn with_rename<S: ToString>(&mut self, rename: S) -> &mut Self {
557 self.rename = Some(rename.to_string());
558 self
559 }
560
561 pub fn with_derive<S: ToString>(&mut self, derive: S) -> &mut Self {
563 self.derives.push(derive.to_string());
564 self
565 }
566}
567
568impl TypeSpace {
569 pub fn new(settings: &TypeSpaceSettings) -> Self {
571 let mut cache = SchemaCache::default();
572
573 settings.convert.iter().for_each(
574 |TypeSpaceConversion {
575 schema,
576 type_name,
577 impls,
578 }| {
579 cache.insert(schema, type_name, impls);
580 },
581 );
582
583 Self {
584 settings: settings.clone(),
585 cache,
586 ..Default::default()
587 }
588 }
589
590 pub fn add_ref_types<I, S>(&mut self, type_defs: I) -> Result<()>
600 where
601 I: IntoIterator<Item = (S, Schema)>,
602 S: AsRef<str>,
603 {
604 self.add_ref_types_impl(
605 type_defs
606 .into_iter()
607 .map(|(key, schema)| (RefKey::Def(key.as_ref().to_string()), schema)),
608 )
609 }
610
611 fn add_ref_types_impl<I>(&mut self, type_defs: I) -> Result<()>
612 where
613 I: IntoIterator<Item = (RefKey, Schema)>,
614 {
615 let definitions = type_defs.into_iter().collect::<Vec<_>>();
617
618 let base_id = self.next_id;
621 let def_len = definitions.len() as u64;
622 self.next_id += def_len;
623
624 for (index, (ref_name, schema)) in definitions.iter().enumerate() {
625 self.ref_to_id
626 .insert(ref_name.clone(), TypeId(base_id + index as u64));
627 self.definitions.insert(ref_name.clone(), schema.clone());
628 }
629
630 for (index, (ref_name, schema)) in definitions.into_iter().enumerate() {
635 info!(
636 "converting type: {:?} with schema {}",
637 ref_name,
638 serde_json::to_string(&schema).unwrap()
639 );
640
641 let type_id = TypeId(base_id + index as u64);
644
645 let maybe_replace = match &ref_name {
646 RefKey::Root => None,
647 RefKey::Def(def_name) => {
648 let check_name = sanitize(def_name, Case::Pascal);
649 self.settings.replace.get(&check_name)
650 }
651 };
652
653 match maybe_replace {
654 None => {
655 let type_name = if let RefKey::Def(name) = ref_name {
656 Name::Required(name.clone())
657 } else {
658 Name::Unknown
659 };
660 self.convert_ref_type(type_name, schema, type_id)?
661 }
662
663 Some(replace_type) => {
664 let type_entry = TypeEntry::new_native(
665 replace_type.replace_type.clone(),
666 &replace_type.impls.clone(),
667 );
668 self.id_to_entry.insert(type_id, type_entry);
669 }
670 }
671 }
672
673 self.break_cycles(base_id..base_id + def_len);
676
677 for index in base_id..self.next_id {
679 let type_id = TypeId(index);
680 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
681 type_entry.finalize(self)?;
682 self.id_to_entry.insert(type_id, type_entry);
683 }
684
685 Ok(())
686 }
687
688 fn convert_ref_type(&mut self, type_name: Name, schema: Schema, type_id: TypeId) -> Result<()> {
689 let (mut type_entry, metadata) = self.convert_schema(type_name.clone(), &schema)?;
690 let default = metadata
691 .as_ref()
692 .and_then(|m| m.default.as_ref())
693 .cloned()
694 .map(WrappedValue::new);
695 let type_entry = match &mut type_entry.details {
696 TypeEntryDetails::Enum(details) => {
698 details.default = default;
699 type_entry
700 }
701 TypeEntryDetails::Struct(details) => {
702 details.default = default;
703 type_entry
704 }
705 TypeEntryDetails::Newtype(details) => {
706 details.default = default;
707 type_entry
708 }
709
710 TypeEntryDetails::Reference(type_id) => TypeEntryNewtype::from_metadata(
715 self,
716 type_name,
717 metadata,
718 type_id.clone(),
719 schema.clone(),
720 ),
721
722 TypeEntryDetails::Native(native) if native.name_match(&type_name) => type_entry,
723
724 _ => {
727 info!(
728 "type alias {:?} {}\n{:?}",
729 type_name,
730 serde_json::to_string_pretty(&schema).unwrap(),
731 metadata
732 );
733 let subtype_id = self.assign_type(type_entry);
734 TypeEntryNewtype::from_metadata(
735 self,
736 type_name,
737 metadata,
738 subtype_id,
739 schema.clone(),
740 )
741 }
742 };
743 if let Some(entry_name) = type_entry.name() {
745 self.name_to_id.insert(entry_name.clone(), type_id.clone());
746 }
747 self.id_to_entry.insert(type_id, type_entry);
748 Ok(())
749 }
750
751 pub fn add_type(&mut self, schema: &Schema) -> Result<TypeId> {
754 self.add_type_with_name(schema, None)
755 }
756
757 pub fn add_type_with_name(
760 &mut self,
761 schema: &Schema,
762 name_hint: Option<String>,
763 ) -> Result<TypeId> {
764 let base_id = self.next_id;
765
766 let name = match name_hint {
767 Some(s) => Name::Suggested(s),
768 None => Name::Unknown,
769 };
770 let (type_id, _) = self.id_for_schema(name, schema)?;
771
772 for index in base_id..self.next_id {
774 let type_id = TypeId(index);
775 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
776 type_entry.finalize(self)?;
777 self.id_to_entry.insert(type_id, type_entry);
778 }
779
780 Ok(type_id)
781 }
782
783 pub fn add_root_schema(&mut self, schema: RootSchema) -> Result<Option<TypeId>> {
787 let RootSchema {
788 meta_schema: _,
789 schema,
790 definitions,
791 } = schema;
792
793 let mut defs = definitions
794 .into_iter()
795 .map(|(key, schema)| (RefKey::Def(key), schema))
796 .collect::<Vec<_>>();
797
798 let root_type = schema
800 .metadata
801 .as_ref()
802 .and_then(|m| m.title.as_ref())
803 .is_some();
804
805 if root_type {
806 defs.push((RefKey::Root, schema.into()));
807 }
808
809 self.add_ref_types_impl(defs)?;
810
811 if root_type {
812 Ok(self.ref_to_id.get(&RefKey::Root).cloned())
813 } else {
814 Ok(None)
815 }
816 }
817
818 pub fn get_type(&self, type_id: &TypeId) -> Result<Type<'_>> {
820 let type_entry = self.id_to_entry.get(type_id).ok_or(Error::InvalidTypeId)?;
821 Ok(Type {
822 type_space: self,
823 type_entry,
824 })
825 }
826
827 pub fn uses_chrono(&self) -> bool {
829 self.uses_chrono
830 }
831
832 pub fn uses_regress(&self) -> bool {
834 self.uses_regress
835 }
836
837 pub fn uses_serde_json(&self) -> bool {
839 self.uses_serde_json
840 }
841
842 pub fn uses_uuid(&self) -> bool {
844 self.uses_uuid
845 }
846
847 pub fn iter_types(&self) -> impl Iterator<Item = Type<'_>> {
850 self.id_to_entry.values().map(move |type_entry| Type {
851 type_space: self,
852 type_entry,
853 })
854 }
855
856 pub fn to_stream(&self) -> TokenStream {
858 let mut output = OutputSpace::default();
859
860 output.add_item(
863 output::OutputSpaceMod::Error,
864 "",
865 quote! {
866 pub struct ConversionError(::std::borrow::Cow<'static, str>);
868
869 impl ::std::error::Error for ConversionError {}
870 impl ::std::fmt::Display for ConversionError {
871 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
872 -> Result<(), ::std::fmt::Error>
873 {
874 ::std::fmt::Display::fmt(&self.0, f)
875 }
876 }
877
878 impl ::std::fmt::Debug for ConversionError {
879 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
880 -> Result<(), ::std::fmt::Error>
881 {
882 ::std::fmt::Debug::fmt(&self.0, f)
883 }
884 }
885 impl From<&'static str> for ConversionError {
886 fn from(value: &'static str) -> Self {
887 Self(value.into())
888 }
889 }
890 impl From<String> for ConversionError {
891 fn from(value: String) -> Self {
892 Self(value.into())
893 }
894 }
895 },
896 );
897
898 self.id_to_entry
900 .values()
901 .for_each(|type_entry| type_entry.output(self, &mut output));
902
903 self.defaults
905 .iter()
906 .for_each(|x| output.add_item(output::OutputSpaceMod::Defaults, "", x.into()));
907
908 output.into_stream()
909 }
910
911 fn assign(&mut self) -> TypeId {
913 let id = TypeId(self.next_id);
914 self.next_id += 1;
915 id
916 }
917
918 fn assign_type(&mut self, ty: TypeEntry) -> TypeId {
923 if let TypeEntryDetails::Reference(type_id) = ty.details {
924 type_id
925 } else if let Some(name) = ty.name() {
926 if let Some(type_id) = self.name_to_id.get(name) {
936 type_id.clone()
942 } else {
943 let type_id = self.assign();
944 self.name_to_id.insert(name.clone(), type_id.clone());
945 self.id_to_entry.insert(type_id.clone(), ty);
946 type_id
947 }
948 } else if let Some(type_id) = self.type_to_id.get(&ty.details) {
949 type_id.clone()
950 } else {
951 let type_id = self.assign();
952 self.type_to_id.insert(ty.details.clone(), type_id.clone());
953 self.id_to_entry.insert(type_id.clone(), ty);
954 type_id
955 }
956 }
957
958 fn id_for_schema<'a>(
963 &mut self,
964 type_name: Name,
965 schema: &'a Schema,
966 ) -> Result<(TypeId, &'a Option<Box<Metadata>>)> {
967 let (mut type_entry, metadata) = self.convert_schema(type_name, schema)?;
968 if let Some(metadata) = metadata {
969 let default = metadata.default.clone().map(WrappedValue::new);
970 match &mut type_entry.details {
971 TypeEntryDetails::Enum(details) => {
972 details.default = default;
973 }
974 TypeEntryDetails::Struct(details) => {
975 details.default = default;
976 }
977 TypeEntryDetails::Newtype(details) => {
978 details.default = default;
979 }
980 _ => (),
981 }
982 }
983 let type_id = self.assign_type(type_entry);
984 Ok((type_id, metadata))
985 }
986
987 fn id_to_option(&mut self, id: &TypeId) -> TypeId {
989 self.assign_type(TypeEntryDetails::Option(id.clone()).into())
990 }
991
992 fn type_to_option(&mut self, ty: TypeEntry) -> TypeEntry {
994 TypeEntryDetails::Option(self.assign_type(ty)).into()
995 }
996
997 fn id_to_box(&mut self, id: &TypeId) -> TypeId {
999 self.assign_type(TypeEntryDetails::Box(id.clone()).into())
1000 }
1001}
1002
1003impl ToTokens for TypeSpace {
1004 fn to_tokens(&self, tokens: &mut TokenStream) {
1005 tokens.extend(self.to_stream())
1006 }
1007}
1008
1009impl Type<'_> {
1010 pub fn name(&self) -> String {
1012 let Type {
1013 type_space,
1014 type_entry,
1015 } = self;
1016 type_entry.type_name(type_space)
1017 }
1018
1019 pub fn ident(&self) -> TokenStream {
1022 let Type {
1023 type_space,
1024 type_entry,
1025 } = self;
1026 type_entry.type_ident(type_space, &type_space.settings.type_mod)
1027 }
1028
1029 pub fn parameter_ident(&self) -> TokenStream {
1033 let Type {
1034 type_space,
1035 type_entry,
1036 } = self;
1037 type_entry.type_parameter_ident(type_space, None)
1038 }
1039
1040 pub fn parameter_ident_with_lifetime(&self, lifetime: &str) -> TokenStream {
1045 let Type {
1046 type_space,
1047 type_entry,
1048 } = self;
1049 type_entry.type_parameter_ident(type_space, Some(lifetime))
1050 }
1051
1052 pub fn describe(&self) -> String {
1054 self.type_entry.describe()
1055 }
1056
1057 pub fn details(&self) -> TypeDetails<'_> {
1059 match &self.type_entry.details {
1060 TypeEntryDetails::Enum(details) => TypeDetails::Enum(TypeEnum { details }),
1062 TypeEntryDetails::Struct(details) => TypeDetails::Struct(TypeStruct { details }),
1063 TypeEntryDetails::Newtype(details) => TypeDetails::Newtype(TypeNewtype { details }),
1064
1065 TypeEntryDetails::Option(type_id) => TypeDetails::Option(type_id.clone()),
1067 TypeEntryDetails::Vec(type_id) => TypeDetails::Vec(type_id.clone()),
1068 TypeEntryDetails::Map(key_id, value_id) => {
1069 TypeDetails::Map(key_id.clone(), value_id.clone())
1070 }
1071 TypeEntryDetails::Set(type_id) => TypeDetails::Set(type_id.clone()),
1072 TypeEntryDetails::Box(type_id) => TypeDetails::Box(type_id.clone()),
1073 TypeEntryDetails::Tuple(types) => TypeDetails::Tuple(Box::new(types.iter().cloned())),
1074 TypeEntryDetails::Array(type_id, length) => {
1075 TypeDetails::Array(type_id.clone(), *length)
1076 }
1077
1078 TypeEntryDetails::Unit => TypeDetails::Unit,
1080 TypeEntryDetails::Native(TypeEntryNative {
1081 type_name: name, ..
1082 })
1083 | TypeEntryDetails::Integer(name)
1084 | TypeEntryDetails::Float(name) => TypeDetails::Builtin(name.as_str()),
1085 TypeEntryDetails::Boolean => TypeDetails::Builtin("bool"),
1086 TypeEntryDetails::String => TypeDetails::String,
1087 TypeEntryDetails::JsonValue => TypeDetails::Builtin("::serde_json::Value"),
1088
1089 TypeEntryDetails::Reference(_) => unreachable!(),
1091 }
1092 }
1093
1094 pub fn has_impl(&self, impl_name: TypeSpaceImpl) -> bool {
1096 let Type {
1097 type_space,
1098 type_entry,
1099 } = self;
1100 type_entry.has_impl(type_space, impl_name)
1101 }
1102
1103 pub fn builder(&self) -> Option<TokenStream> {
1105 let Type {
1106 type_space,
1107 type_entry,
1108 } = self;
1109
1110 if !type_space.settings.struct_builder {
1111 return None;
1112 }
1113
1114 match &type_entry.details {
1115 TypeEntryDetails::Struct(type_entry::TypeEntryStruct { name, .. }) => {
1116 match &type_space.settings.type_mod {
1117 Some(type_mod) => {
1118 let type_mod = format_ident!("{}", type_mod);
1119 let type_name = format_ident!("{}", name);
1120 Some(quote! { #type_mod :: builder :: #type_name })
1121 }
1122 None => {
1123 let type_name = format_ident!("{}", name);
1124 Some(quote! { builder :: #type_name })
1125 }
1126 }
1127 }
1128 _ => None,
1129 }
1130 }
1131}
1132
1133impl<'a> TypeEnum<'a> {
1134 pub fn variants(&'a self) -> impl Iterator<Item = (&'a str, TypeEnumVariant<'a>)> {
1136 self.variants_info().map(|info| (info.name, info.details))
1137 }
1138
1139 pub fn variants_info(&'a self) -> impl Iterator<Item = TypeEnumVariantInfo<'a>> {
1141 self.details.variants.iter().map(move |variant| {
1142 let details = match &variant.details {
1143 type_entry::VariantDetails::Simple => TypeEnumVariant::Simple,
1144 type_entry::VariantDetails::Item(type_id) => {
1147 TypeEnumVariant::Tuple(vec![type_id.clone()])
1148 }
1149 type_entry::VariantDetails::Tuple(types) => TypeEnumVariant::Tuple(types.clone()),
1150 type_entry::VariantDetails::Struct(properties) => TypeEnumVariant::Struct(
1151 properties
1152 .iter()
1153 .map(|prop| (prop.name.as_str(), prop.type_id.clone()))
1154 .collect(),
1155 ),
1156 };
1157 TypeEnumVariantInfo {
1158 name: variant.ident_name.as_ref().unwrap(),
1159 description: variant.description.as_deref(),
1160 details,
1161 }
1162 })
1163 }
1164}
1165
1166impl<'a> TypeStruct<'a> {
1167 pub fn properties(&'a self) -> impl Iterator<Item = (&'a str, TypeId)> {
1169 self.details
1170 .properties
1171 .iter()
1172 .map(move |prop| (prop.name.as_str(), prop.type_id.clone()))
1173 }
1174
1175 pub fn properties_info(&'a self) -> impl Iterator<Item = TypeStructPropInfo<'a>> {
1177 self.details
1178 .properties
1179 .iter()
1180 .map(move |prop| TypeStructPropInfo {
1181 name: prop.name.as_str(),
1182 description: prop.description.as_deref(),
1183 required: matches!(&prop.state, StructPropertyState::Required),
1184 type_id: prop.type_id.clone(),
1185 })
1186 }
1187}
1188
1189impl TypeNewtype<'_> {
1190 pub fn inner(&self) -> TypeId {
1192 self.details.type_id.clone()
1193 }
1194}
1195
1196#[cfg(test)]
1197mod tests {
1198 use schema::Schema;
1199 use schemars::{schema_for, JsonSchema};
1200 use serde::Serialize;
1201 use serde_json::json;
1202 use std::collections::HashSet;
1203
1204 use crate::{
1205 output::OutputSpace,
1206 test_util::validate_output,
1207 type_entry::{TypeEntryEnum, VariantDetails},
1208 Name, TypeEntryDetails, TypeSpace, TypeSpaceSettings,
1209 };
1210
1211 #[allow(dead_code)]
1212 #[derive(Serialize, JsonSchema)]
1213 struct Blah {
1214 blah: String,
1215 }
1216
1217 #[allow(dead_code)]
1218 #[derive(Serialize, JsonSchema)]
1219 #[serde(rename_all = "camelCase")]
1220 enum E {
1223 A,
1225 B,
1227 C(Blah),
1230 D {
1232 dd: String,
1234 },
1235 }
1243
1244 #[allow(dead_code)]
1245 #[derive(JsonSchema)]
1246 #[serde(rename_all = "camelCase")]
1247 struct Foo {
1248 #[serde(default)]
1250 bar: Option<String>,
1251 baz_baz: i32,
1252 e: E,
1254 }
1255
1256 #[test]
1257 fn test_simple() {
1258 let schema = schema_for!(Foo);
1259 println!("{:#?}", schema);
1260 let mut type_space = TypeSpace::default();
1261 type_space.add_ref_types(schema.definitions).unwrap();
1262 let (ty, _) = type_space
1263 .convert_schema_object(
1264 Name::Unknown,
1265 &schemars::schema::Schema::Object(schema.schema.clone()),
1266 &schema.schema,
1267 )
1268 .unwrap();
1269
1270 println!("{:#?}", ty);
1271
1272 let mut output = OutputSpace::default();
1273 ty.output(&type_space, &mut output);
1274 println!("{}", output.into_stream());
1275
1276 for ty in type_space.id_to_entry.values() {
1277 println!("{:#?}", ty);
1278 let mut output = OutputSpace::default();
1279 ty.output(&type_space, &mut output);
1280 println!("{}", output.into_stream());
1281 }
1282 }
1283
1284 #[test]
1285 fn test_external_references() {
1286 let schema = json!({
1287 "$schema": "http://json-schema.org/draft-04/schema#",
1288 "definitions": {
1289 "somename": {
1290 "$ref": "#/definitions/someothername",
1291 "required": [ "someproperty" ]
1292 },
1293 "someothername": {
1294 "type": "object",
1295 "properties": {
1296 "someproperty": {
1297 "type": "string"
1298 }
1299 }
1300 }
1301 }
1302 });
1303 let schema = serde_json::from_value(schema).unwrap();
1304 println!("{:#?}", schema);
1305 let settings = TypeSpaceSettings::default();
1306 let mut type_space = TypeSpace::new(&settings);
1307 type_space.add_root_schema(schema).unwrap();
1308 let tokens = type_space.to_stream().to_string();
1309 println!("{}", tokens);
1310 assert!(tokens
1311 .contains(" pub struct Somename { pub someproperty : :: std :: string :: String , }"))
1312 }
1313
1314 #[test]
1315 fn test_convert_enum_string() {
1316 #[allow(dead_code)]
1317 #[derive(JsonSchema)]
1318 #[serde(rename_all = "camelCase")]
1319 enum SimpleEnum {
1320 DotCom,
1321 Grizz,
1322 Kenneth,
1323 }
1324
1325 let schema = schema_for!(SimpleEnum);
1326 println!("{:#?}", schema);
1327
1328 let mut type_space = TypeSpace::default();
1329 type_space.add_ref_types(schema.definitions).unwrap();
1330 let (ty, _) = type_space
1331 .convert_schema_object(
1332 Name::Unknown,
1333 &schemars::schema::Schema::Object(schema.schema.clone()),
1334 &schema.schema,
1335 )
1336 .unwrap();
1337
1338 match ty.details {
1339 TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) => {
1340 for variant in &variants {
1341 assert_eq!(variant.details, VariantDetails::Simple);
1342 }
1343 let var_names = variants
1344 .iter()
1345 .map(|variant| variant.ident_name.as_ref().unwrap().clone())
1346 .collect::<HashSet<_>>();
1347 assert_eq!(
1348 var_names,
1349 ["DotCom", "Grizz", "Kenneth",]
1350 .iter()
1351 .map(ToString::to_string)
1352 .collect::<HashSet<_>>()
1353 );
1354 }
1355 _ => {
1356 let mut output = OutputSpace::default();
1357 ty.output(&type_space, &mut output);
1358 println!("{}", output.into_stream());
1359 panic!();
1360 }
1361 }
1362 }
1363
1364 #[test]
1365 fn test_string_enum_with_null() {
1366 let original_schema = json!({ "$ref": "xxx"});
1367 let enum_values = vec![
1368 json!("Shadrach"),
1369 json!("Meshach"),
1370 json!("Abednego"),
1371 json!(null),
1372 ];
1373
1374 let mut type_space = TypeSpace::default();
1375 let (te, _) = type_space
1376 .convert_enum_string(
1377 Name::Required("OnTheGo".to_string()),
1378 &serde_json::from_value(original_schema).unwrap(),
1379 &None,
1380 &enum_values,
1381 None,
1382 )
1383 .unwrap();
1384
1385 if let TypeEntryDetails::Option(id) = &te.details {
1386 let ote = type_space.id_to_entry.get(id).unwrap();
1387 if let TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) = &ote.details {
1388 let variants = variants
1389 .iter()
1390 .map(|v| match v.details {
1391 VariantDetails::Simple => v.ident_name.as_ref().unwrap().clone(),
1392 _ => panic!("unexpected variant type"),
1393 })
1394 .collect::<HashSet<_>>();
1395
1396 assert_eq!(
1397 variants,
1398 enum_values
1399 .iter()
1400 .flat_map(|j| j.as_str().map(ToString::to_string))
1401 .collect::<HashSet<_>>()
1402 );
1403 } else {
1404 panic!("not the sub-type we expected {:#?}", te)
1405 }
1406 } else {
1407 panic!("not the type we expected {:#?}", te)
1408 }
1409 }
1410
1411 #[test]
1412 fn test_alias() {
1413 #[allow(dead_code)]
1414 #[derive(JsonSchema, Schema)]
1415 struct Stuff(Vec<String>);
1416
1417 #[allow(dead_code)]
1418 #[derive(JsonSchema, Schema)]
1419 struct Things {
1420 a: String,
1421 b: Stuff,
1422 }
1423
1424 validate_output::<Things>();
1425 }
1426
1427 #[test]
1428 fn test_builder_name() {
1429 #[allow(dead_code)]
1430 #[derive(JsonSchema)]
1431 struct TestStruct {
1432 x: u32,
1433 }
1434
1435 let mut type_space = TypeSpace::default();
1436 let schema = schema_for!(TestStruct);
1437 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1438 let ty = type_space.get_type(&type_id).unwrap();
1439
1440 assert!(ty.builder().is_none());
1441
1442 let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_struct_builder(true));
1443 let schema = schema_for!(TestStruct);
1444 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1445 let ty = type_space.get_type(&type_id).unwrap();
1446
1447 assert_eq!(
1448 ty.builder().map(|ts| ts.to_string()),
1449 Some("builder :: TestStruct".to_string())
1450 );
1451
1452 let mut type_space = TypeSpace::new(
1453 TypeSpaceSettings::default()
1454 .with_type_mod("types")
1455 .with_struct_builder(true),
1456 );
1457 let schema = schema_for!(TestStruct);
1458 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1459 let ty = type_space.get_type(&type_id).unwrap();
1460
1461 assert_eq!(
1462 ty.builder().map(|ts| ts.to_string()),
1463 Some("types :: builder :: TestStruct".to_string())
1464 );
1465
1466 #[allow(dead_code)]
1467 #[derive(JsonSchema)]
1468 enum TestEnum {
1469 X,
1470 Y,
1471 }
1472 let mut type_space = TypeSpace::new(
1473 TypeSpaceSettings::default()
1474 .with_type_mod("types")
1475 .with_struct_builder(true),
1476 );
1477 let schema = schema_for!(TestEnum);
1478 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1479 let ty = type_space.get_type(&type_id).unwrap();
1480 assert!(ty.builder().is_none());
1481 }
1482}