Skip to content

Commit

Permalink
fixes #175 + add DataType::Unknown
Browse files Browse the repository at this point in the history
  • Loading branch information
oscartbeaumont committed Nov 22, 2023
1 parent 82b5441 commit 0470a6a
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/datatype/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub struct DefOpts<'a> {
pub enum DataType {
// Always inlined
Any,
Unknown,
Primitive(PrimitiveType),
Literal(LiteralType),
/// Either a `Set` or a `Vec`
Expand Down
2 changes: 2 additions & 0 deletions src/lang/ts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ pub fn datatype(conf: &ExportConfig, typ: &DataType, type_map: &TypeMap) -> Outp
pub(crate) fn datatype_inner(ctx: ExportContext, typ: &DataType, type_map: &TypeMap) -> Output {
Ok(match &typ {
DataType::Any => ANY.into(),
DataType::Unknown => UNKNOWN.into(),
DataType::Primitive(p) => {
let ctx = ctx.with(PathItem::Type(p.to_rust_str().into()));
match p {
Expand Down Expand Up @@ -658,6 +659,7 @@ pub(crate) fn sanitise_type_name(ctx: ExportContext, loc: NamedLocation, ident:
}

const ANY: &str = "any";
const UNKNOWN: &str = "unknown";
const NUMBER: &str = "number";
const STRING: &str = "string";
const BOOLEAN: &str = "boolean";
Expand Down
2 changes: 1 addition & 1 deletion src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ fn validate_internally_tag_enum_datatype(
// TODO: Maybe make this a public utility?
fn resolve_generics(mut dt: DataType, generics: &Vec<(GenericType, DataType)>) -> DataType {
match dt {
DataType::Primitive(_) | DataType::Literal(_) | DataType::Any => dt,
DataType::Primitive(_) | DataType::Literal(_) | DataType::Any | DataType::Unknown => dt,
DataType::List(v) => DataType::List(Box::new(resolve_generics(*v, generics))),
DataType::Nullable(v) => DataType::Nullable(Box::new(resolve_generics(*v, generics))),
DataType::Map(v) => DataType::Map(Box::new({
Expand Down
105 changes: 101 additions & 4 deletions src/static_types.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
use std::fmt::Debug;

use crate::{DataType, DefOpts, Type};

/// A type that is unconstructable but is typed as `any` in TypeScript.
/// Easily convert a non-Specta type into a Specta compatible type.
/// This will be typed as `any` in Typescript.
///
/// WARNING: When used with `Option<Any<T>>`, Typescript will not prompt you about nullability checks as `any | null` is coalesced to `any` in Typescript.
///
/// This can be use like the following:
/// # Examples
///
/// This can be used as a type override.
/// ```rust
/// use serde::Serialize;
/// use specta::{Type, Any};
Expand All @@ -13,10 +20,100 @@ use crate::{DataType, DefOpts, Type};
/// pub field: String,
/// }
/// ```
pub enum Any {}
///
/// Or it can be used as a wrapper type.
/// ```rust
/// use serde::Serialize;
/// use specta::{Type, Any};
///
/// #[derive(Serialize, Type)]
/// pub struct Demo {
/// pub field: Any<String>,
/// }
/// ```
pub struct Any<T = ()>(T);

impl Type for Any {
impl<T> Type for Any<T> {
fn inline(_: DefOpts, _: &[DataType]) -> DataType {
DataType::Any
}
}

impl<T: Debug> Debug for Any<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Any").field(&self.0).finish()
}
}

impl<T: Clone> Clone for Any<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}

#[cfg(feature = "serde")]
impl<T: serde::Serialize> serde::Serialize for Any<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
T::serialize(&self.0, serializer)
}
}

/// Easily convert a non-Specta type into a Specta compatible type.
/// This will be typed as `unknown` in Typescript.
///
/// # Examples
///
/// This can be used as a type override.
/// ```rust
/// use serde::Serialize;
/// use specta::{Type, Unknown};
///
/// #[derive(Serialize, Type)]
/// pub struct Demo {
/// #[specta(type = Unknown)]
/// pub field: String,
/// }
/// ```
///
/// Or it can be used as a wrapper type.
/// ```rust
/// use serde::Serialize;
/// use specta::{Type, Unknown};
///
/// #[derive(Serialize, Type)]
/// pub struct Demo {
/// pub field: Unknown<String>,
/// }
/// ```
pub struct Unknown<T = ()>(T);

impl<T> Type for Unknown<T> {
fn inline(_: DefOpts, _: &[DataType]) -> DataType {
DataType::Unknown
}
}

impl<T: Debug> Debug for Unknown<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Any").field(&self.0).finish()
}
}

impl<T: Clone> Clone for Unknown<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}

#[cfg(feature = "serde")]
impl<T: serde::Serialize> serde::Serialize for Unknown<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
T::serialize(&self.0, serializer)
}
}
1 change: 1 addition & 0 deletions tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod reserved_keywords;
mod selection;
mod serde;
mod sid;
mod static_types;
mod transparent;
pub mod ts;
mod ts_rs;
Expand Down
12 changes: 12 additions & 0 deletions tests/static_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use specta::{Any, Unknown};

use crate::ts::assert_ts;

#[test]
fn static_types() {
assert_ts!(Any, "any");
assert_ts!(Unknown, "unknown");

assert_ts!(Any<String>, "any");
assert_ts!(Unknown<String>, "unknown");
}

0 comments on commit 0470a6a

Please sign in to comment.