Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[toolchain]
channel = "1.81.0"
channel = "1.85.0"
profile = "default"
1 change: 1 addition & 0 deletions typify-impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ struct TypeSpaceConversion {
#[non_exhaustive]
pub enum TypeSpaceImpl {
FromStr,
FromStringIrrefutable,
Display,
Default,
}
Expand Down
74 changes: 65 additions & 9 deletions typify-impl/src/type_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,16 @@ pub(crate) struct TypeEntryEnum {
pub schema: SchemaWrapper,
}

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

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
Expand Down Expand Up @@ -315,12 +320,14 @@ impl TypeEntryEnum {
.iter()
.all(|variant| matches!(variant.details, VariantDetails::Simple)))
.then_some(TypeEntryEnumImpl::AllSimpleVariants),
// Untagged and all variants impl FromStr.
// Untagged and all variants impl FromStr, but none **is** a
// String (i.e. irrefutably).
untagged_newtype_variants(
type_space,
&self.tag_type,
&self.variants,
TypeSpaceImpl::FromStr,
Some(TypeSpaceImpl::FromStringIrrefutable),
)
.then_some(TypeEntryEnumImpl::UntaggedFromStr),
// Untagged and all variants impl Display.
Expand All @@ -329,8 +336,11 @@ impl TypeEntryEnum {
&self.tag_type,
&self.variants,
TypeSpaceImpl::Display,
None,
)
.then_some(TypeEntryEnumImpl::UntaggedDisplay),
untagged_newtype_string(type_space, &self.tag_type, &self.variants)
.then_some(TypeEntryEnumImpl::UntaggedFromStringIrrefutable),
]
.into_iter()
.flatten()
Expand Down Expand Up @@ -597,7 +607,6 @@ impl TypeEntry {
match &self.details {
TypeEntryDetails::Enum(details) => match impl_name {
TypeSpaceImpl::Default => details.default.is_some(),

TypeSpaceImpl::FromStr => {
details
.bespoke_impls
Expand All @@ -614,6 +623,9 @@ impl TypeEntry {
.bespoke_impls
.contains(&TypeEntryEnumImpl::UntaggedDisplay)
}
TypeSpaceImpl::FromStringIrrefutable => details
.bespoke_impls
.contains(&TypeEntryEnumImpl::UntaggedFromStringIrrefutable),
},

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

TypeEntryDetails::Boolean => true,
TypeEntryDetails::Integer(_) => true,
TypeEntryDetails::Float(_) => true,
TypeEntryDetails::String => true,
TypeEntryDetails::Boolean => match impl_name {
TypeSpaceImpl::Default | TypeSpaceImpl::FromStr | TypeSpaceImpl::Display => true,
TypeSpaceImpl::FromStringIrrefutable => false,
},
TypeEntryDetails::Integer(_) => match impl_name {
TypeSpaceImpl::Default | TypeSpaceImpl::FromStr | TypeSpaceImpl::Display => true,
TypeSpaceImpl::FromStringIrrefutable => false,
},

TypeEntryDetails::Float(_) => match impl_name {
TypeSpaceImpl::Default | TypeSpaceImpl::FromStr | TypeSpaceImpl::Display => true,
TypeSpaceImpl::FromStringIrrefutable => false,
},
TypeEntryDetails::String => match impl_name {
TypeSpaceImpl::Default
| TypeSpaceImpl::FromStr
| TypeSpaceImpl::Display
| TypeSpaceImpl::FromStringIrrefutable => true,
},

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

/// Returns true iff...
/// - the enum is untagged
/// - all variants are 1-item tuple-types (aka newtype variants)
/// - all variants are single items (aka newtype variants)
/// - the type of the newtype variant implements the required trait
fn untagged_newtype_variants(
type_space: &TypeSpace,
tag_type: &EnumTagType,
variants: &[Variant],
req_impl: TypeSpaceImpl,
neg_impl: Option<TypeSpaceImpl>,
) -> bool {
tag_type == &EnumTagType::Untagged
&& variants.iter().all(|variant| {
// If the variant is a one-element tuple...
// If the variant is a single item...
match &variant.details {
VariantDetails::Item(type_id) => Some(type_id),
_ => None,
Expand All @@ -2015,6 +2043,34 @@ fn untagged_newtype_variants(
let type_entry = type_space.id_to_entry.get(type_id).unwrap();
// ... and its type has the required impl
type_entry.has_impl(type_space, req_impl)
&& neg_impl
.is_none_or(|neg_impl| !type_entry.has_impl(type_space, neg_impl))
},
)
})
}

/// Returns true iff...
/// - the enum is untagged
/// - **any** variant is a single items **and** it is irrefutably a string
fn untagged_newtype_string(
type_space: &TypeSpace,
tag_type: &EnumTagType,
variants: &[Variant],
) -> bool {
tag_type == &EnumTagType::Untagged
&& variants.iter().any(|variant| {
// If the variant is a single item...
match &variant.details {
VariantDetails::Item(type_id) => Some(type_id),
_ => None,
}
.map_or_else(
|| false,
|type_id| {
let type_entry = type_space.id_to_entry.get(type_id).unwrap();
// ... and it is irrefutably a string
type_entry.has_impl(type_space, TypeSpaceImpl::FromStringIrrefutable)
},
)
})
Expand Down
6 changes: 3 additions & 3 deletions typify-impl/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,15 +861,15 @@ impl StringValidator {
pub fn is_valid<S: AsRef<str>>(&self, s: S) -> bool {
self.max_length
.as_ref()
.map_or(true, |max| s.as_ref().len() as u32 <= *max)
.is_none_or(|max| s.as_ref().len() as u32 <= *max)
&& self
.min_length
.as_ref()
.map_or(true, |min| s.as_ref().len() as u32 >= *min)
.is_none_or(|min| s.as_ref().len() as u32 >= *min)
&& self
.pattern
.as_ref()
.map_or(true, |pattern| pattern.find(s.as_ref()).is_some())
.is_none_or(|pattern| pattern.find(s.as_ref()).is_some())
}
}

Expand Down
110 changes: 0 additions & 110 deletions typify-impl/tests/vega.out
Original file line number Diff line number Diff line change
Expand Up @@ -77087,40 +77087,6 @@ impl ::std::convert::From<&Self> for OnTriggerItemRemove {
value.clone()
}
}
impl ::std::str::FromStr for OnTriggerItemRemove {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if let Ok(v) = value.parse() {
Ok(Self::Variant0(v))
} else if let Ok(v) = value.parse() {
Ok(Self::Variant1(v))
} else {
Err("string conversion failed for all variants".into())
}
}
}
impl ::std::convert::TryFrom<&str> for OnTriggerItemRemove {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for OnTriggerItemRemove {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for OnTriggerItemRemove {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::fmt::Display for OnTriggerItemRemove {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match self {
Expand Down Expand Up @@ -87598,42 +87564,6 @@ impl ::std::convert::From<&Self> for ScaleDataVariant2FieldsItemVariant1Item {
value.clone()
}
}
impl ::std::str::FromStr for ScaleDataVariant2FieldsItemVariant1Item {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if let Ok(v) = value.parse() {
Ok(Self::Variant0(v))
} else if let Ok(v) = value.parse() {
Ok(Self::Variant1(v))
} else if let Ok(v) = value.parse() {
Ok(Self::Variant2(v))
} else {
Err("string conversion failed for all variants".into())
}
}
}
impl ::std::convert::TryFrom<&str> for ScaleDataVariant2FieldsItemVariant1Item {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ScaleDataVariant2FieldsItemVariant1Item {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ScaleDataVariant2FieldsItemVariant1Item {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::fmt::Display for ScaleDataVariant2FieldsItemVariant1Item {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match self {
Expand Down Expand Up @@ -91177,46 +91107,6 @@ impl ::std::convert::From<&Self> for ScaleVariant1RangeVariant3Variant2FieldsIte
value.clone()
}
}
impl ::std::str::FromStr for ScaleVariant1RangeVariant3Variant2FieldsItemVariant1Item {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if let Ok(v) = value.parse() {
Ok(Self::Variant0(v))
} else if let Ok(v) = value.parse() {
Ok(Self::Variant1(v))
} else if let Ok(v) = value.parse() {
Ok(Self::Variant2(v))
} else {
Err("string conversion failed for all variants".into())
}
}
}
impl ::std::convert::TryFrom<&str> for ScaleVariant1RangeVariant3Variant2FieldsItemVariant1Item {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for ScaleVariant1RangeVariant3Variant2FieldsItemVariant1Item
{
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for ScaleVariant1RangeVariant3Variant2FieldsItemVariant1Item
{
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::fmt::Display for ScaleVariant1RangeVariant3Variant2FieldsItemVariant1Item {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match self {
Expand Down
34 changes: 0 additions & 34 deletions typify/tests/schemas/multiple-instance-types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,40 +49,6 @@ impl ::std::convert::From<&Self> for IntOrStr {
value.clone()
}
}
impl ::std::str::FromStr for IntOrStr {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if let Ok(v) = value.parse() {
Ok(Self::String(v))
} else if let Ok(v) = value.parse() {
Ok(Self::Integer(v))
} else {
Err("string conversion failed for all variants".into())
}
}
}
impl ::std::convert::TryFrom<&str> for IntOrStr {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for IntOrStr {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for IntOrStr {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::fmt::Display for IntOrStr {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match self {
Expand Down
6 changes: 4 additions & 2 deletions typify/tests/schemas/various-enums.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@
]
},
"Ipv4Net": {
"type": "string"
"type": "string",
"pattern": ".*"
},
"Ipv6Net": {
"type": "string"
"type": "string",
"pattern": ".*"
},
"NullStringEnumWithUnknownFormat": {
"type": [
Expand Down
Loading
Loading