Skip to content

Commit

Permalink
Seal NamedDataType
Browse files Browse the repository at this point in the history
  • Loading branch information
oscartbeaumont committed Aug 31, 2023
1 parent 27b6ff3 commit f0b5d65
Show file tree
Hide file tree
Showing 12 changed files with 209 additions and 147 deletions.
18 changes: 9 additions & 9 deletions macros/src/type/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,14 @@ pub fn named_data_type_wrapper(
};

quote! {
#crate_ref::NamedDataType {
name: #name.into(),
sid: Some(SID),
impl_location: Some(IMPL_LOCATION),
comments: #comments,
export: #should_export,
deprecated: #deprecated,
item: #t
}
#crate_ref::internal::construct::named_data_type(
#name.into(),
#comments,
#deprecated,
SID,
IMPL_LOCATION,
#should_export,
#t
)
}
}
84 changes: 3 additions & 81 deletions src/datatype/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ use std::{

mod r#enum;
mod literal;
mod named;
mod primitive;
mod r#struct;
mod tuple;

pub use literal::*;
pub use named::*;
pub use primitive::*;
pub use r#enum::*;
pub use r#struct::*;
pub use tuple::*;

use crate::{ImplLocation, TypeSid};
use crate::TypeSid;

/// A map used to store the types "discovered" while exporting a type.
/// You can iterate over this to export all types which the type/s you exported references on.
Expand Down Expand Up @@ -58,86 +60,6 @@ pub enum DataType {
Generic(GenericType),
}

/// A named type represents a non-primitive type capable of being exported as it's own named entity.
#[derive(Debug, Clone, PartialEq)]
pub struct NamedDataType {
/// The name of the type
pub name: Cow<'static, str>,
/// The Specta ID for the type. The value for this should come from the `sid!();` macro.
pub sid: Option<TypeSid>,
/// The code location where this type is implemented. Used for error reporting.
pub impl_location: Option<ImplLocation>,
/// Rust documentation comments on the type
pub comments: Vec<Cow<'static, str>>,
/// DEPRECATED. This is not used and shouldn't be. Will be removed in Specta v2!
pub export: Option<bool>,
/// The Rust deprecated comment if the type is deprecated.
pub deprecated: Option<Cow<'static, str>>,
/// the actual type definition.
pub item: NamedDataTypeItem,
}

impl From<NamedDataType> for DataType {
fn from(t: NamedDataType) -> Self {
Self::Named(t)
}
}

/// The possible types for a [`NamedDataType`].
///
/// This type will model the type of the Rust type that is being exported but be aware of the following:
/// ```rust
/// #[derive(serde::Serialize)]
/// struct Demo {}
/// // is: NamedDataTypeItem::Struct
/// // typescript: `{}`
///
/// #[derive(serde::Serialize)]
/// struct Demo2();
/// // is: NamedDataTypeItem::Tuple(TupleType::Unnamed)
/// // typescript: `[]`
///
/// #[derive(specta::Type)]
/// struct Demo3;
///// is: NamedDataTypeItem::Tuple(TupleType::Named(_))
/// // typescript: `null`
/// ```
#[derive(Debug, Clone, PartialEq)]
pub enum NamedDataTypeItem {
/// Represents an Rust struct with named fields
Struct(StructType),
/// Represents an Rust enum
Enum(EnumType),
/// Represents an Rust struct with unnamed fields
Tuple(TupleType),
}

impl NamedDataTypeItem {
/// Converts a [`NamedDataTypeItem`] into a [`DataType`]
pub fn datatype(self) -> DataType {
match self {
Self::Struct(o) => o.into(),
Self::Enum(e) => e.into(),
Self::Tuple(t) => t.into(),
}
}

/// Returns the generics arguments for the type
pub fn generics(&self) -> Vec<GenericType> {
match self {
// Named struct
Self::Struct(StructType { generics, .. }) => generics.clone(),
// Enum
Self::Enum(e) => e.generics().clone(),
// Struct with unnamed fields
Self::Tuple(tuple) => match tuple {
TupleType::Unnamed => vec![],
TupleType::Named { generics, .. } => generics.clone(),
},
}
}
}

/// A reference to a [`DataType`] that can be used before a type is resolved in order to
/// support recursive types without causing an infinite loop.
///
Expand Down
126 changes: 126 additions & 0 deletions src/datatype/named.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use std::borrow::Cow;

use crate::{DataType, EnumType, GenericType, ImplLocation, StructType, TupleType, TypeSid};

/// A NamedDataTypeImpl includes extra information which is only available for [NamedDataType]'s that come from a real Rust type.
#[derive(Debug, Clone, PartialEq)]
pub struct NamedDataTypeExt {
/// The Specta ID for the type. The value for this should come from the `sid!();` macro.
pub(crate) sid: TypeSid,
/// The code location where this type is implemented. Used for error reporting.
pub(crate) impl_location: ImplLocation,
// TODO: Undeprecate this and handle it properly!
// TODO: Support different export contexts
/// DEPRECATED. This is not used and shouldn't be. Will be removed in Specta v2!
pub(crate) export: Option<bool>,
}

impl NamedDataTypeExt {
pub fn sid(&self) -> &TypeSid {

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

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> src/datatype/named.rs:19:5 | 19 | pub fn sid(&self) -> &TypeSid { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> src/lib.rs:58:58 | 58 | #![warn(clippy::all, clippy::unwrap_used, clippy::panic, missing_docs)] | ^^^^^^^^^^^^
&self.sid
}

pub fn impl_location(&self) -> &ImplLocation {

Check warning on line 23 in src/datatype/named.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> src/datatype/named.rs:23:5 | 23 | pub fn impl_location(&self) -> &ImplLocation { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
&self.impl_location
}

pub fn export(&self) -> &Option<bool> {

Check warning on line 27 in src/datatype/named.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> src/datatype/named.rs:27:5 | 27 | pub fn export(&self) -> &Option<bool> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
&self.export
}
}

/// A named type represents a non-primitive type capable of being exported as it's own named entity.
#[derive(Debug, Clone, PartialEq)]
pub struct NamedDataType {
/// The name of the type
pub(crate) name: Cow<'static, str>,
/// Rust documentation comments on the type
pub(crate) comments: Vec<Cow<'static, str>>,
/// The Rust deprecated comment if the type is deprecated.
pub(crate) deprecated: Option<Cow<'static, str>>,
/// Extra information that comes from a real Rust type (using the `Type` macro).
/// This will be `None` when constructing [NamedDataType] using `StructType::to_named` or `TupleType::to_named` since those types do not correspond to actual Rust types.
pub(crate) ext: Option<NamedDataTypeExt>,
/// the actual type definition.
// This field is public because we match on it in flattening code. // TODO: Review if this can be fixed when reviewing the flattening logic/error handling
pub item: NamedDataTypeItem,
}

impl NamedDataType {
pub fn name(&self) -> &Cow<'static, str> {

Check warning on line 50 in src/datatype/named.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> src/datatype/named.rs:50:5 | 50 | pub fn name(&self) -> &Cow<'static, str> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
&self.name
}

pub fn comments(&self) -> &Vec<Cow<'static, str>> {

Check warning on line 54 in src/datatype/named.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> src/datatype/named.rs:54:5 | 54 | pub fn comments(&self) -> &Vec<Cow<'static, str>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
&self.comments
}

pub fn deprecated(&self) -> &Option<Cow<'static, str>> {

Check warning on line 58 in src/datatype/named.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> src/datatype/named.rs:58:5 | 58 | pub fn deprecated(&self) -> &Option<Cow<'static, str>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
&self.deprecated
}

pub fn ext(&self) -> &Option<NamedDataTypeExt> {

Check warning on line 62 in src/datatype/named.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> src/datatype/named.rs:62:5 | 62 | pub fn ext(&self) -> &Option<NamedDataTypeExt> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
&self.ext
}
}

impl From<NamedDataType> for DataType {
fn from(t: NamedDataType) -> Self {
Self::Named(t)
}
}

/// The possible types for a [`NamedDataType`].
///
/// This type will model the type of the Rust type that is being exported but be aware of the following:
/// ```rust
/// #[derive(serde::Serialize)]
/// struct Demo {}
/// // is: NamedDataTypeItem::Struct
/// // typescript: `{}`
///
/// #[derive(serde::Serialize)]
/// struct Demo2();
/// // is: NamedDataTypeItem::Tuple(TupleType::Unnamed)
/// // typescript: `[]`
///
/// #[derive(specta::Type)]
/// struct Demo3;
///// is: NamedDataTypeItem::Tuple(TupleType::Named(_))
/// // typescript: `null`
/// ```
#[derive(Debug, Clone, PartialEq)]
pub enum NamedDataTypeItem {
/// Represents an Rust struct with named fields
Struct(StructType),
/// Represents an Rust enum
Enum(EnumType),
/// Represents an Rust struct with unnamed fields
Tuple(TupleType),
}

impl NamedDataTypeItem {
/// Converts a [`NamedDataTypeItem`] into a [`DataType`]
pub fn datatype(self) -> DataType {
match self {
Self::Struct(o) => o.into(),
Self::Enum(e) => e.into(),
Self::Tuple(t) => t.into(),
}
}

/// Returns the generics arguments for the type
pub fn generics(&self) -> Vec<GenericType> {
match self {
// Named struct
Self::Struct(StructType { generics, .. }) => generics.clone(),
// Enum
Self::Enum(e) => e.generics().clone(),
// Struct with unnamed fields
Self::Tuple(tuple) => match tuple {
TupleType::Unnamed => vec![],
TupleType::Named { generics, .. } => generics.clone(),
},
}
}
}
4 changes: 1 addition & 3 deletions src/datatype/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@ impl StructType {
pub fn to_named(self, name: impl Into<Cow<'static, str>>) -> NamedDataType {
NamedDataType {
name: name.into(),
sid: None,
impl_location: None,
comments: vec![],
export: None,
deprecated: None,
ext: None,
item: NamedDataTypeItem::Struct(self),
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/datatype/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,9 @@ impl TupleType {
pub fn to_named(self, name: impl Into<Cow<'static, str>>) -> NamedDataType {
NamedDataType {
name: name.into(),
sid: None,
impl_location: None,
comments: vec![],
export: None,
deprecated: None,
ext: None,
item: NamedDataTypeItem::Tuple(self),
}
}
Expand Down
26 changes: 16 additions & 10 deletions src/export/ts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ pub fn ts_with_cfg(path: &str, conf: &ExportConfig) -> Result<(), TsExportError>
let types = get_types()?
.into_iter()

Check warning on line 20 in src/export/ts.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `export::export::TypesIter`

warning: useless conversion to the same type: `export::export::TypesIter` --> src/export/ts.rs:19:17 | 19 | let types = get_types()? | _________________^ 20 | | .into_iter() | |____________________^ help: consider removing `.into_iter()`: `get_types()?` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `#[warn(clippy::useless_conversion)]` implied by `#[warn(clippy::all)]`
.filter(|(_, v)| match v {
Some(v) => v.export.unwrap_or(export_by_default),
Some(v) => v
.ext()
.as_ref()
.and_then(|ext| ext.export)
.unwrap_or(export_by_default),
None => {
unreachable!("Placeholder type should never be returned from the Specta functions!")
}
Expand All @@ -31,15 +35,17 @@ pub fn ts_with_cfg(path: &str, conf: &ExportConfig) -> Result<(), TsExportError>
for (sid, dt) in &types {
match dt {
Some(dt) => {
if let Some((existing_sid, existing_impl_location)) =
map.insert(dt.name.clone(), (sid, dt.impl_location))
{
if existing_sid != sid {
return Err(TsExportError::DuplicateTypeName(
dt.name.clone(),
dt.impl_location,
existing_impl_location,
));
if let Some(ext) = &dt.ext {
if let Some((existing_sid, existing_impl_location)) =
map.insert(dt.name.clone(), (sid, ext.impl_location))
{
if existing_sid != sid {
return Err(TsExportError::DuplicateTypeName(
dt.name.clone(),
ext.impl_location,
existing_impl_location,
));
}
}
}
}
Expand Down
24 changes: 23 additions & 1 deletion src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub use specta_macros::fn_datatype;
pub mod construct {
use std::borrow::Cow;

use crate::datatype::*;
use crate::{datatype::*, ImplLocation, TypeSid};

pub const fn r#struct(
generics: Vec<GenericType>,
Expand All @@ -28,4 +28,26 @@ pub mod construct {
tag,
}
}

pub const fn named_data_type(
name: Cow<'static, str>,
comments: Vec<Cow<'static, str>>,
deprecated: Option<Cow<'static, str>>,
sid: TypeSid,
impl_location: ImplLocation,
export: Option<bool>,
item: NamedDataTypeItem,
) -> NamedDataType {
NamedDataType {
name,
comments,
deprecated,
ext: Some(NamedDataTypeExt {
sid,
impl_location,
export,
}),
item,
}
}
}
10 changes: 3 additions & 7 deletions src/lang/ts/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl fmt::Display for NamedLocation {

/// The error type for the TypeScript exporter.
#[derive(Error, Debug)]
#[allow(missing_docs)]
#[non_exhaustive]
pub enum TsExportError {
#[error("Attempted to export '{0}' but Specta configuration forbids exporting BigInt types (i64, u64, i128, u128) because we don't know if your se/deserializer supports it. You can change this behavior by editing your `ExportConfiguration`!")]
BigIntForbidden(ExportPath),

Check warning on line 34 in src/lang/ts/error.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a variant

warning: missing documentation for a variant --> src/lang/ts/error.rs:34:5 | 34 | BigIntForbidden(ExportPath), | ^^^^^^^^^^^^^^^
Expand All @@ -38,12 +38,8 @@ pub enum TsExportError {
ForbiddenName(NamedLocation, ExportPath, &'static str),

Check warning on line 38 in src/lang/ts/error.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a variant

warning: missing documentation for a variant --> src/lang/ts/error.rs:38:5 | 38 | ForbiddenName(NamedLocation, ExportPath, &'static str), | ^^^^^^^^^^^^^
#[error("Attempted to export '{0}' with tagging but the type is not tagged.")]
InvalidTagging(ExportPath),

Check warning on line 40 in src/lang/ts/error.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a variant

warning: missing documentation for a variant --> src/lang/ts/error.rs:40:5 | 40 | InvalidTagging(ExportPath), | ^^^^^^^^^^^^^^
#[error("Unable to export type named '{0}' from locations '{:?}' '{:?}'", .1.map(|v| v.as_str()), .2.map(|v| v.as_str()))]
DuplicateTypeName(
Cow<'static, str>,
Option<ImplLocation>,
Option<ImplLocation>,
),
#[error("Unable to export type named '{0}' from locations '{:?}' '{:?}'", .1.as_str(), .2.as_str())]
DuplicateTypeName(Cow<'static, str>, ImplLocation, ImplLocation),

Check warning on line 42 in src/lang/ts/error.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a variant

warning: missing documentation for a variant --> src/lang/ts/error.rs:42:5 | 42 | DuplicateTypeName(Cow<'static, str>, ImplLocation, ImplLocation), | ^^^^^^^^^^^^^^^^^
#[error("{0}")]
SpectaExportError(#[from] ExportError),

Check warning on line 44 in src/lang/ts/error.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a variant

warning: missing documentation for a variant --> src/lang/ts/error.rs:44:5 | 44 | SpectaExportError(#[from] ExportError), | ^^^^^^^^^^^^^^^^^
#[error("IO error: {0}")]
Expand Down
2 changes: 1 addition & 1 deletion src/type/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub enum TypeCategory {

/// Type exporting errors.
#[derive(Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[allow(missing_docs)]
#[non_exhaustive]
pub enum ExportError {
#[error("Atemmpted to export type defined at '{}' but encountered error: {1}", .0.as_str())]
InvalidType(ImplLocation, &'static str),

Check warning on line 27 in src/type/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a variant

warning: missing documentation for a variant --> src/type/mod.rs:27:5 | 27 | InvalidType(ImplLocation, &'static str), | ^^^^^^^^^^^
Expand Down
Loading

0 comments on commit f0b5d65

Please sign in to comment.