Skip to content

Commit

Permalink
Seal EnumType
Browse files Browse the repository at this point in the history
  • Loading branch information
oscartbeaumont committed Aug 31, 2023
1 parent 0e1da19 commit 3bef15f
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 84 deletions.
35 changes: 16 additions & 19 deletions macros/src/type/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,22 +181,19 @@ pub fn parse_enum(
let (enum_impl, can_flatten) = match repr {
Tagged::Untagged => (
quote! {
#crate_ref::EnumType::Untagged {
generics: vec![#(#definition_generics),*],
variants: vec![#(#variant_types),*],
}
#crate_ref::internal::construct::untagged_enum(vec![#(#variant_types),*], vec![#(#definition_generics),*])
},
data.variants
.iter()
.any(|v| matches!(&v.fields, Fields::Unit | Fields::Named(_))),
),
Tagged::Externally => (
quote! {
#crate_ref::EnumType::Tagged {
generics: vec![#(#definition_generics),*],
variants: vec![#((#variant_names.into(), #variant_types)),*],
repr: #crate_ref::EnumRepr::External,
}
#crate_ref::internal::construct::tagged_enum(
vec![#((#variant_names.into(), #variant_types)),*],
vec![#(#definition_generics),*],
#crate_ref::EnumRepr::External
)
},
data.variants.iter().any(|v| match &v.fields {
Fields::Unnamed(f) if f.unnamed.len() == 1 => true,
Expand All @@ -206,21 +203,21 @@ pub fn parse_enum(
),
Tagged::Adjacently { tag, content } => (
quote! {
#crate_ref::EnumType::Tagged {
generics: vec![#(#definition_generics),*],
variants: vec![#((#variant_names.into(), #variant_types)),*],
repr: #crate_ref::EnumRepr::Adjacent { tag: #tag.into(), content: #content.into() },
}
#crate_ref::internal::construct::tagged_enum(
vec![#((#variant_names.into(), #variant_types)),*],
vec![#(#definition_generics),*],
#crate_ref::EnumRepr::Adjacent { tag: #tag.into(), content: #content.into() }
)
},
true,
),
Tagged::Internally { tag } => (
quote! {
#crate_ref::EnumType::Tagged {
generics: vec![#(#definition_generics),*],
variants: vec![#((#variant_names.into(), #variant_types)),*],
repr: #crate_ref::EnumRepr::Internal { tag: #tag.into() },
}
#crate_ref::internal::construct::tagged_enum(
vec![#((#variant_names.into(), #variant_types)),*],
vec![#(#definition_generics),*],
#crate_ref::EnumRepr::Internal { tag: #tag.into() },
)
},
data.variants
.iter()
Expand Down
73 changes: 57 additions & 16 deletions src/datatype/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,65 @@ use crate::{
ExportError, GenericType, ImplLocation,
};

#[derive(Debug, Clone, PartialEq)]
pub struct UntaggedEnum {

Check warning on line 9 in src/datatype/enum.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a struct

warning: missing documentation for a struct --> src/datatype/enum.rs:9:1 | 9 | pub struct UntaggedEnum { | ^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> src/lib.rs:58:58 | 58 | #![warn(clippy::all, clippy::unwrap_used, clippy::panic, missing_docs)] | ^^^^^^^^^^^^
pub(crate) variants: Vec<EnumVariant>,
pub(crate) generics: Vec<GenericType>,
}

impl UntaggedEnum {
pub fn variants(&self) -> impl Iterator<Item = &EnumVariant> {

Check warning on line 15 in src/datatype/enum.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> src/datatype/enum.rs:15:5 | 15 | pub fn variants(&self) -> impl Iterator<Item = &EnumVariant> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.variants.iter()
}

pub fn generics(&self) -> impl Iterator<Item = &GenericType> {

Check warning on line 19 in src/datatype/enum.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> src/datatype/enum.rs:19:5 | 19 | pub fn generics(&self) -> impl Iterator<Item = &GenericType> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.generics.iter()
}
}

impl Into<EnumType> for UntaggedEnum {

Check warning on line 24 in src/datatype/enum.rs

View workflow job for this annotation

GitHub Actions / clippy

an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true

warning: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true --> src/datatype/enum.rs:24:1 | 24 | impl Into<EnumType> for UntaggedEnum { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into = note: `#[warn(clippy::from_over_into)]` implied by `#[warn(clippy::all)]` help: replace the `Into` implementation with `From<datatype::r#enum::UntaggedEnum>` | 24 ~ impl From<UntaggedEnum> for EnumType { 25 ~ fn from(val: UntaggedEnum) -> Self { 26 ~ EnumType::Untagged(val) |
fn into(self) -> EnumType {
EnumType::Untagged(self)
}
}

#[derive(Debug, Clone, PartialEq)]
pub struct TaggedEnum {

Check warning on line 31 in src/datatype/enum.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a struct

warning: missing documentation for a struct --> src/datatype/enum.rs:31:1 | 31 | pub struct TaggedEnum { | ^^^^^^^^^^^^^^^^^^^^^
pub(crate) variants: Vec<(Cow<'static, str>, EnumVariant)>,
pub(crate) generics: Vec<GenericType>,
pub(crate) repr: EnumRepr,
}

impl TaggedEnum {
pub fn variants(&self) -> impl Iterator<Item = &(Cow<'static, str>, EnumVariant)> {

Check warning on line 38 in src/datatype/enum.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> src/datatype/enum.rs:38:5 | 38 | pub fn variants(&self) -> impl Iterator<Item = &(Cow<'static, str>, EnumVariant)> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.variants.iter()
}

pub fn generics(&self) -> impl Iterator<Item = &GenericType> {

Check warning on line 42 in src/datatype/enum.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> src/datatype/enum.rs:42:5 | 42 | pub fn generics(&self) -> impl Iterator<Item = &GenericType> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.generics.iter()
}

pub fn repr(&self) -> &EnumRepr {

Check warning on line 46 in src/datatype/enum.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> src/datatype/enum.rs:46:5 | 46 | pub fn repr(&self) -> &EnumRepr { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
&self.repr
}
}

impl Into<EnumType> for TaggedEnum {

Check warning on line 51 in src/datatype/enum.rs

View workflow job for this annotation

GitHub Actions / clippy

an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true

warning: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true --> src/datatype/enum.rs:51:1 | 51 | impl Into<EnumType> for TaggedEnum { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into help: replace the `Into` implementation with `From<datatype::r#enum::TaggedEnum>` | 51 ~ impl From<TaggedEnum> for EnumType { 52 ~ fn from(val: TaggedEnum) -> Self { 53 ~ EnumType::Tagged(val) |
fn into(self) -> EnumType {
EnumType::Tagged(self)
}
}

/// Enum type which dictates how the enum is represented.
///
/// The tagging refers to the [Serde concept](https://serde.rs/enum-representations.html).
///
/// [`Untagged`](EnumType::Untagged) is here rather than in [`EnumRepr`] as it is the only enum representation that does not have tags on its variants.
/// Separating it allows for better typesafety since `variants` doesn't have to be a [`Vec`] of tuples.
#[derive(Debug, Clone, PartialEq)]
#[allow(missing_docs)]
pub enum EnumType {
Untagged {
variants: Vec<EnumVariant>,
generics: Vec<GenericType>,
},
Tagged {
variants: Vec<(Cow<'static, str>, EnumVariant)>,
generics: Vec<GenericType>,
repr: EnumRepr,
},
Untagged(UntaggedEnum),

Check warning on line 65 in src/datatype/enum.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a variant

warning: missing documentation for a variant --> src/datatype/enum.rs:65:5 | 65 | Untagged(UntaggedEnum), | ^^^^^^^^
Tagged(TaggedEnum),

Check warning on line 66 in src/datatype/enum.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a variant

warning: missing documentation for a variant --> src/datatype/enum.rs:66:5 | 66 | Tagged(TaggedEnum), | ^^^^^^
}

impl From<EnumType> for DataType {
Expand All @@ -34,23 +75,23 @@ impl From<EnumType> for DataType {
impl EnumType {
pub(crate) fn generics(&self) -> &Vec<GenericType> {
match self {
Self::Untagged { generics, .. } => generics,
Self::Tagged { generics, .. } => generics,
Self::Untagged(UntaggedEnum { generics, .. }) => generics,
Self::Tagged(TaggedEnum { generics, .. }) => generics,
}
}

pub(crate) fn variants_len(&self) -> usize {
match self {
Self::Untagged { variants, .. } => variants.len(),
Self::Tagged { variants, .. } => variants.len(),
Self::Untagged(UntaggedEnum { variants, .. }) => variants.len(),
Self::Tagged(TaggedEnum { variants, .. }) => variants.len(),
}
}

/// An enum may contain variants which are invalid and will cause a runtime errors during serialize/deserialization.
/// This function will filter them out so types can be exported for valid variants.
pub fn make_flattenable(&mut self, impl_location: ImplLocation) -> Result<(), ExportError> {
match self {
Self::Untagged { variants, .. } => {
Self::Untagged(UntaggedEnum { variants, .. }) => {
variants.iter().try_for_each(|v| match v {
EnumVariant::Unit => Ok(()),
EnumVariant::Named(_) => Ok(()),
Expand All @@ -60,7 +101,7 @@ impl EnumType {
)),
})?;
}
Self::Tagged { variants, repr, .. } => {
Self::Tagged(TaggedEnum { variants, repr, .. }) => {
variants.iter().try_for_each(|(_, v)| {
match repr {
EnumRepr::External => match v {
Expand Down
25 changes: 14 additions & 11 deletions src/datatype/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,21 @@ impl From<GenericType> for DataType {

impl<T: Into<DataType> + 'static> From<Vec<T>> for DataType {
fn from(t: Vec<T>) -> Self {
DataType::Enum(EnumType::Untagged {
variants: t
.into_iter()
.map(|t| {
EnumVariant::Unnamed(TupleType::Named {
fields: vec![t.into()],
generics: vec![],
DataType::Enum(
UntaggedEnum {
variants: t
.into_iter()
.map(|t| {
EnumVariant::Unnamed(TupleType::Named {
fields: vec![t.into()],
generics: vec![],
})
})
})
.collect(),
generics: vec![],
})
.collect(),
generics: vec![],
}
.into(),
)
}
}

Expand Down
16 changes: 16 additions & 0 deletions src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,20 @@ pub mod construct {
generics,
}
}

pub const fn untagged_enum(variants: Vec<EnumVariant>, generics: Vec<GenericType>) -> EnumType {
EnumType::Untagged(UntaggedEnum { variants, generics })
}

pub const fn tagged_enum(
variants: Vec<(Cow<'static, str>, EnumVariant)>,
generics: Vec<GenericType>,
repr: EnumRepr,
) -> EnumType {
EnumType::Tagged(TaggedEnum {
variants,
generics,
repr,
})
}
}
4 changes: 2 additions & 2 deletions src/lang/ts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ fn enum_datatype(
}

Ok(match e {
EnumType::Tagged { variants, repr, .. } => {
EnumType::Tagged(TaggedEnum { variants, repr, .. }) => {
let mut variants = variants
.iter()
.map(|(variant_name, variant)| {
Expand Down Expand Up @@ -376,7 +376,7 @@ fn enum_datatype(
variants.dedup();
variants.join(" | ")
}
EnumType::Untagged { variants, .. } => {
EnumType::Untagged(UntaggedEnum { variants, .. }) => {
let mut variants = variants
.iter()
.map(|variant| {
Expand Down
78 changes: 42 additions & 36 deletions src/type/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,23 +255,26 @@ const _: () = {

impl Type for serde_json::Number {
fn inline(_: DefOpts, _: &[DataType]) -> Result<DataType, ExportError> {
Ok(DataType::Enum(EnumType::Untagged {
variants: vec![
EnumVariant::Unnamed(TupleType::Named {
fields: vec![DataType::Primitive(PrimitiveType::f64)],
generics: vec![],
}),
EnumVariant::Unnamed(TupleType::Named {
fields: vec![DataType::Primitive(PrimitiveType::i64)],
generics: vec![],
}),
EnumVariant::Unnamed(TupleType::Named {
fields: vec![DataType::Primitive(PrimitiveType::u64)],
generics: vec![],
}),
],
generics: vec![],
}))
Ok(DataType::Enum(
UntaggedEnum {
variants: vec![
EnumVariant::Unnamed(TupleType::Named {
fields: vec![DataType::Primitive(PrimitiveType::f64)],
generics: vec![],
}),
EnumVariant::Unnamed(TupleType::Named {
fields: vec![DataType::Primitive(PrimitiveType::i64)],
generics: vec![],
}),
EnumVariant::Unnamed(TupleType::Named {
fields: vec![DataType::Primitive(PrimitiveType::u64)],
generics: vec![],
}),
],
generics: vec![],
}
.into(),
))
}
}
};
Expand All @@ -298,23 +301,26 @@ const _: () = {

impl Type for serde_yaml::Number {
fn inline(_: DefOpts, _: &[DataType]) -> Result<DataType, ExportError> {
Ok(DataType::Enum(EnumType::Untagged {
variants: vec![
EnumVariant::Unnamed(TupleType::Named {
fields: vec![DataType::Primitive(PrimitiveType::f64)],
generics: vec![],
}),
EnumVariant::Unnamed(TupleType::Named {
fields: vec![DataType::Primitive(PrimitiveType::i64)],
generics: vec![],
}),
EnumVariant::Unnamed(TupleType::Named {
fields: vec![DataType::Primitive(PrimitiveType::u64)],
generics: vec![],
}),
],
generics: vec![],
}))
Ok(DataType::Enum(
UntaggedEnum {
variants: vec![
EnumVariant::Unnamed(TupleType::Named {
fields: vec![DataType::Primitive(PrimitiveType::f64)],
generics: vec![],
}),
EnumVariant::Unnamed(TupleType::Named {
fields: vec![DataType::Primitive(PrimitiveType::i64)],
generics: vec![],
}),
EnumVariant::Unnamed(TupleType::Named {
fields: vec![DataType::Primitive(PrimitiveType::u64)],
generics: vec![],
}),
],
generics: vec![],
}
.into(),
))
}
}
};
Expand Down Expand Up @@ -505,7 +511,7 @@ impl_as!(url::Url as String);
#[cfg(feature = "either")]
impl<L: Type, R: Type> Type for either::Either<L, R> {
fn inline(opts: DefOpts, generics: &[DataType]) -> Result<DataType, ExportError> {
Ok(DataType::Enum(EnumType::Untagged {
Ok(DataType::Enum(EnumType::Untagged(UntaggedEnum {
variants: vec![
EnumVariant::Unnamed(TupleType::Named {
fields: vec![L::inline(
Expand All @@ -529,6 +535,6 @@ impl<L: Type, R: Type> Type for either::Either<L, R> {
}),
],
generics: vec![],
}))
})))
}
}

0 comments on commit 3bef15f

Please sign in to comment.