Skip to content

Commit a23ae57

Browse files
authored
fix #827 (#861)
1 parent 67efcce commit a23ae57

File tree

8 files changed

+160
-239
lines changed

8 files changed

+160
-239
lines changed

rust-toolchain.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[toolchain]
2-
channel = "1.81.0"
2+
channel = "1.85.0"
33
profile = "default"

typify-impl/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ struct TypeSpaceConversion {
367367
#[non_exhaustive]
368368
pub enum TypeSpaceImpl {
369369
FromStr,
370+
FromStringIrrefutable,
370371
Display,
371372
Default,
372373
}

typify-impl/src/type_entry.rs

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,16 @@ pub(crate) struct TypeEntryEnum {
4646
pub schema: SchemaWrapper,
4747
}
4848

49+
/// Cached attributes that (mostly) result in customized impl generation.
4950
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
5051
pub(crate) enum TypeEntryEnumImpl {
5152
AllSimpleVariants,
5253
UntaggedFromStr,
5354
UntaggedDisplay,
55+
/// This is a cached marker to let us know that at least one of the
56+
/// variants is irrefutably a string. There is currently no associated
57+
/// implementation that we generate.
58+
UntaggedFromStringIrrefutable,
5459
}
5560

5661
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
@@ -315,12 +320,14 @@ impl TypeEntryEnum {
315320
.iter()
316321
.all(|variant| matches!(variant.details, VariantDetails::Simple)))
317322
.then_some(TypeEntryEnumImpl::AllSimpleVariants),
318-
// Untagged and all variants impl FromStr.
323+
// Untagged and all variants impl FromStr, but none **is** a
324+
// String (i.e. irrefutably).
319325
untagged_newtype_variants(
320326
type_space,
321327
&self.tag_type,
322328
&self.variants,
323329
TypeSpaceImpl::FromStr,
330+
Some(TypeSpaceImpl::FromStringIrrefutable),
324331
)
325332
.then_some(TypeEntryEnumImpl::UntaggedFromStr),
326333
// Untagged and all variants impl Display.
@@ -329,8 +336,11 @@ impl TypeEntryEnum {
329336
&self.tag_type,
330337
&self.variants,
331338
TypeSpaceImpl::Display,
339+
None,
332340
)
333341
.then_some(TypeEntryEnumImpl::UntaggedDisplay),
342+
untagged_newtype_string(type_space, &self.tag_type, &self.variants)
343+
.then_some(TypeEntryEnumImpl::UntaggedFromStringIrrefutable),
334344
]
335345
.into_iter()
336346
.flatten()
@@ -597,7 +607,6 @@ impl TypeEntry {
597607
match &self.details {
598608
TypeEntryDetails::Enum(details) => match impl_name {
599609
TypeSpaceImpl::Default => details.default.is_some(),
600-
601610
TypeSpaceImpl::FromStr => {
602611
details
603612
.bespoke_impls
@@ -614,6 +623,9 @@ impl TypeEntry {
614623
.bespoke_impls
615624
.contains(&TypeEntryEnumImpl::UntaggedDisplay)
616625
}
626+
TypeSpaceImpl::FromStringIrrefutable => details
627+
.bespoke_impls
628+
.contains(&TypeEntryEnumImpl::UntaggedFromStringIrrefutable),
617629
},
618630

619631
TypeEntryDetails::Struct(details) => match impl_name {
@@ -627,7 +639,7 @@ impl TypeEntry {
627639
(TypeEntryNewtypeConstraints::None, _) => {
628640
// TODO this is a lucky kludge that will need to be removed
629641
// once we have proper handling of reference cycles (i.e.
630-
// as opposed to containment cycles... which we also do not
642+
// as opposed to containment cycles... which we **do**
631643
// handle correctly). In particular output_newtype calls
632644
// this to determine if it should produce a FromStr impl.
633645
// This implementation could be infinitely recursive for a
@@ -685,10 +697,25 @@ impl TypeEntry {
685697
}
686698
}
687699

688-
TypeEntryDetails::Boolean => true,
689-
TypeEntryDetails::Integer(_) => true,
690-
TypeEntryDetails::Float(_) => true,
691-
TypeEntryDetails::String => true,
700+
TypeEntryDetails::Boolean => match impl_name {
701+
TypeSpaceImpl::Default | TypeSpaceImpl::FromStr | TypeSpaceImpl::Display => true,
702+
TypeSpaceImpl::FromStringIrrefutable => false,
703+
},
704+
TypeEntryDetails::Integer(_) => match impl_name {
705+
TypeSpaceImpl::Default | TypeSpaceImpl::FromStr | TypeSpaceImpl::Display => true,
706+
TypeSpaceImpl::FromStringIrrefutable => false,
707+
},
708+
709+
TypeEntryDetails::Float(_) => match impl_name {
710+
TypeSpaceImpl::Default | TypeSpaceImpl::FromStr | TypeSpaceImpl::Display => true,
711+
TypeSpaceImpl::FromStringIrrefutable => false,
712+
},
713+
TypeEntryDetails::String => match impl_name {
714+
TypeSpaceImpl::Default
715+
| TypeSpaceImpl::FromStr
716+
| TypeSpaceImpl::Display
717+
| TypeSpaceImpl::FromStringIrrefutable => true,
718+
},
692719

693720
TypeEntryDetails::Reference(_) => unreachable!(),
694721
}
@@ -1994,17 +2021,18 @@ fn strings_to_derives<'a>(
19942021

19952022
/// Returns true iff...
19962023
/// - the enum is untagged
1997-
/// - all variants are 1-item tuple-types (aka newtype variants)
2024+
/// - all variants are single items (aka newtype variants)
19982025
/// - the type of the newtype variant implements the required trait
19992026
fn untagged_newtype_variants(
20002027
type_space: &TypeSpace,
20012028
tag_type: &EnumTagType,
20022029
variants: &[Variant],
20032030
req_impl: TypeSpaceImpl,
2031+
neg_impl: Option<TypeSpaceImpl>,
20042032
) -> bool {
20052033
tag_type == &EnumTagType::Untagged
20062034
&& variants.iter().all(|variant| {
2007-
// If the variant is a one-element tuple...
2035+
// If the variant is a single item...
20082036
match &variant.details {
20092037
VariantDetails::Item(type_id) => Some(type_id),
20102038
_ => None,
@@ -2015,6 +2043,34 @@ fn untagged_newtype_variants(
20152043
let type_entry = type_space.id_to_entry.get(type_id).unwrap();
20162044
// ... and its type has the required impl
20172045
type_entry.has_impl(type_space, req_impl)
2046+
&& neg_impl
2047+
.is_none_or(|neg_impl| !type_entry.has_impl(type_space, neg_impl))
2048+
},
2049+
)
2050+
})
2051+
}
2052+
2053+
/// Returns true iff...
2054+
/// - the enum is untagged
2055+
/// - **any** variant is a single items **and** it is irrefutably a string
2056+
fn untagged_newtype_string(
2057+
type_space: &TypeSpace,
2058+
tag_type: &EnumTagType,
2059+
variants: &[Variant],
2060+
) -> bool {
2061+
tag_type == &EnumTagType::Untagged
2062+
&& variants.iter().any(|variant| {
2063+
// If the variant is a single item...
2064+
match &variant.details {
2065+
VariantDetails::Item(type_id) => Some(type_id),
2066+
_ => None,
2067+
}
2068+
.map_or_else(
2069+
|| false,
2070+
|type_id| {
2071+
let type_entry = type_space.id_to_entry.get(type_id).unwrap();
2072+
// ... and it is irrefutably a string
2073+
type_entry.has_impl(type_space, TypeSpaceImpl::FromStringIrrefutable)
20182074
},
20192075
)
20202076
})

typify-impl/src/util.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -861,15 +861,15 @@ impl StringValidator {
861861
pub fn is_valid<S: AsRef<str>>(&self, s: S) -> bool {
862862
self.max_length
863863
.as_ref()
864-
.map_or(true, |max| s.as_ref().len() as u32 <= *max)
864+
.is_none_or(|max| s.as_ref().len() as u32 <= *max)
865865
&& self
866866
.min_length
867867
.as_ref()
868-
.map_or(true, |min| s.as_ref().len() as u32 >= *min)
868+
.is_none_or(|min| s.as_ref().len() as u32 >= *min)
869869
&& self
870870
.pattern
871871
.as_ref()
872-
.map_or(true, |pattern| pattern.find(s.as_ref()).is_some())
872+
.is_none_or(|pattern| pattern.find(s.as_ref()).is_some())
873873
}
874874
}
875875

typify-impl/tests/vega.out

Lines changed: 0 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -77087,40 +77087,6 @@ impl ::std::convert::From<&Self> for OnTriggerItemRemove {
7708777087
value.clone()
7708877088
}
7708977089
}
77090-
impl ::std::str::FromStr for OnTriggerItemRemove {
77091-
type Err = self::error::ConversionError;
77092-
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
77093-
if let Ok(v) = value.parse() {
77094-
Ok(Self::Variant0(v))
77095-
} else if let Ok(v) = value.parse() {
77096-
Ok(Self::Variant1(v))
77097-
} else {
77098-
Err("string conversion failed for all variants".into())
77099-
}
77100-
}
77101-
}
77102-
impl ::std::convert::TryFrom<&str> for OnTriggerItemRemove {
77103-
type Error = self::error::ConversionError;
77104-
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
77105-
value.parse()
77106-
}
77107-
}
77108-
impl ::std::convert::TryFrom<&::std::string::String> for OnTriggerItemRemove {
77109-
type Error = self::error::ConversionError;
77110-
fn try_from(
77111-
value: &::std::string::String,
77112-
) -> ::std::result::Result<Self, self::error::ConversionError> {
77113-
value.parse()
77114-
}
77115-
}
77116-
impl ::std::convert::TryFrom<::std::string::String> for OnTriggerItemRemove {
77117-
type Error = self::error::ConversionError;
77118-
fn try_from(
77119-
value: ::std::string::String,
77120-
) -> ::std::result::Result<Self, self::error::ConversionError> {
77121-
value.parse()
77122-
}
77123-
}
7712477090
impl ::std::fmt::Display for OnTriggerItemRemove {
7712577091
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
7712677092
match self {
@@ -87598,42 +87564,6 @@ impl ::std::convert::From<&Self> for ScaleDataVariant2FieldsItemVariant1Item {
8759887564
value.clone()
8759987565
}
8760087566
}
87601-
impl ::std::str::FromStr for ScaleDataVariant2FieldsItemVariant1Item {
87602-
type Err = self::error::ConversionError;
87603-
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
87604-
if let Ok(v) = value.parse() {
87605-
Ok(Self::Variant0(v))
87606-
} else if let Ok(v) = value.parse() {
87607-
Ok(Self::Variant1(v))
87608-
} else if let Ok(v) = value.parse() {
87609-
Ok(Self::Variant2(v))
87610-
} else {
87611-
Err("string conversion failed for all variants".into())
87612-
}
87613-
}
87614-
}
87615-
impl ::std::convert::TryFrom<&str> for ScaleDataVariant2FieldsItemVariant1Item {
87616-
type Error = self::error::ConversionError;
87617-
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
87618-
value.parse()
87619-
}
87620-
}
87621-
impl ::std::convert::TryFrom<&::std::string::String> for ScaleDataVariant2FieldsItemVariant1Item {
87622-
type Error = self::error::ConversionError;
87623-
fn try_from(
87624-
value: &::std::string::String,
87625-
) -> ::std::result::Result<Self, self::error::ConversionError> {
87626-
value.parse()
87627-
}
87628-
}
87629-
impl ::std::convert::TryFrom<::std::string::String> for ScaleDataVariant2FieldsItemVariant1Item {
87630-
type Error = self::error::ConversionError;
87631-
fn try_from(
87632-
value: ::std::string::String,
87633-
) -> ::std::result::Result<Self, self::error::ConversionError> {
87634-
value.parse()
87635-
}
87636-
}
8763787567
impl ::std::fmt::Display for ScaleDataVariant2FieldsItemVariant1Item {
8763887568
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
8763987569
match self {
@@ -91177,46 +91107,6 @@ impl ::std::convert::From<&Self> for ScaleVariant1RangeVariant3Variant2FieldsIte
9117791107
value.clone()
9117891108
}
9117991109
}
91180-
impl ::std::str::FromStr for ScaleVariant1RangeVariant3Variant2FieldsItemVariant1Item {
91181-
type Err = self::error::ConversionError;
91182-
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
91183-
if let Ok(v) = value.parse() {
91184-
Ok(Self::Variant0(v))
91185-
} else if let Ok(v) = value.parse() {
91186-
Ok(Self::Variant1(v))
91187-
} else if let Ok(v) = value.parse() {
91188-
Ok(Self::Variant2(v))
91189-
} else {
91190-
Err("string conversion failed for all variants".into())
91191-
}
91192-
}
91193-
}
91194-
impl ::std::convert::TryFrom<&str> for ScaleVariant1RangeVariant3Variant2FieldsItemVariant1Item {
91195-
type Error = self::error::ConversionError;
91196-
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
91197-
value.parse()
91198-
}
91199-
}
91200-
impl ::std::convert::TryFrom<&::std::string::String>
91201-
for ScaleVariant1RangeVariant3Variant2FieldsItemVariant1Item
91202-
{
91203-
type Error = self::error::ConversionError;
91204-
fn try_from(
91205-
value: &::std::string::String,
91206-
) -> ::std::result::Result<Self, self::error::ConversionError> {
91207-
value.parse()
91208-
}
91209-
}
91210-
impl ::std::convert::TryFrom<::std::string::String>
91211-
for ScaleVariant1RangeVariant3Variant2FieldsItemVariant1Item
91212-
{
91213-
type Error = self::error::ConversionError;
91214-
fn try_from(
91215-
value: ::std::string::String,
91216-
) -> ::std::result::Result<Self, self::error::ConversionError> {
91217-
value.parse()
91218-
}
91219-
}
9122091110
impl ::std::fmt::Display for ScaleVariant1RangeVariant3Variant2FieldsItemVariant1Item {
9122191111
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
9122291112
match self {

typify/tests/schemas/multiple-instance-types.rs

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -49,40 +49,6 @@ impl ::std::convert::From<&Self> for IntOrStr {
4949
value.clone()
5050
}
5151
}
52-
impl ::std::str::FromStr for IntOrStr {
53-
type Err = self::error::ConversionError;
54-
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
55-
if let Ok(v) = value.parse() {
56-
Ok(Self::String(v))
57-
} else if let Ok(v) = value.parse() {
58-
Ok(Self::Integer(v))
59-
} else {
60-
Err("string conversion failed for all variants".into())
61-
}
62-
}
63-
}
64-
impl ::std::convert::TryFrom<&str> for IntOrStr {
65-
type Error = self::error::ConversionError;
66-
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
67-
value.parse()
68-
}
69-
}
70-
impl ::std::convert::TryFrom<&::std::string::String> for IntOrStr {
71-
type Error = self::error::ConversionError;
72-
fn try_from(
73-
value: &::std::string::String,
74-
) -> ::std::result::Result<Self, self::error::ConversionError> {
75-
value.parse()
76-
}
77-
}
78-
impl ::std::convert::TryFrom<::std::string::String> for IntOrStr {
79-
type Error = self::error::ConversionError;
80-
fn try_from(
81-
value: ::std::string::String,
82-
) -> ::std::result::Result<Self, self::error::ConversionError> {
83-
value.parse()
84-
}
85-
}
8652
impl ::std::fmt::Display for IntOrStr {
8753
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
8854
match self {

typify/tests/schemas/various-enums.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@
4848
]
4949
},
5050
"Ipv4Net": {
51-
"type": "string"
51+
"type": "string",
52+
"pattern": ".*"
5253
},
5354
"Ipv6Net": {
54-
"type": "string"
55+
"type": "string",
56+
"pattern": ".*"
5557
},
5658
"NullStringEnumWithUnknownFormat": {
5759
"type": [

0 commit comments

Comments
 (0)