Skip to content

Commit

Permalink
add support for top level as (#288)
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-bonez committed Apr 2, 2024
1 parent cf450ac commit c862aa6
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 1 deletion.
4 changes: 4 additions & 0 deletions macros/src/attr/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
#[derive(Default)]
pub struct EnumAttr {
crate_rename: Option<Path>,
pub type_as: Option<Type>,
pub type_override: Option<String>,
pub rename_all: Option<Inflection>,
pub rename_all_fields: Option<Inflection>,
Expand Down Expand Up @@ -72,6 +73,7 @@ impl EnumAttr {
&mut self,
EnumAttr {
crate_rename,
type_as,
type_override,
rename_all,
rename_all_fields,
Expand All @@ -87,6 +89,7 @@ impl EnumAttr {
}: EnumAttr,
) {
self.crate_rename = self.crate_rename.take().or(crate_rename);
self.type_as = self.type_as.take().or(type_as);
self.type_override = self.type_override.take().or(type_override);
self.rename = self.rename.take().or(rename);
self.rename_all = self.rename_all.take().or(rename_all);
Expand All @@ -113,6 +116,7 @@ impl EnumAttr {
impl_parse! {
EnumAttr(input, out) {
"crate" => out.crate_rename = Some(parse_assign_from_str(input)?),
"as" => out.type_as = Some(parse_assign_from_str(input)?),
"type" => out.type_override = Some(parse_assign_str(input)?),
"rename" => out.rename = Some(parse_assign_str(input)?),
"rename_all" => out.rename_all = Some(parse_assign_inflection(input)?),
Expand Down
4 changes: 4 additions & 0 deletions macros/src/attr/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
#[derive(Default, Clone)]
pub struct StructAttr {
crate_rename: Option<Path>,
pub type_as: Option<Type>,
pub type_override: Option<String>,
pub rename_all: Option<Inflection>,
pub rename: Option<String>,
Expand Down Expand Up @@ -59,6 +60,7 @@ impl StructAttr {
&mut self,
StructAttr {
crate_rename,
type_as,
type_override,
rename_all,
rename,
Expand All @@ -71,6 +73,7 @@ impl StructAttr {
}: StructAttr,
) {
self.crate_rename = self.crate_rename.take().or(crate_rename);
self.type_as = self.type_as.take().or(type_as);
self.type_override = self.type_override.take().or(type_override);
self.rename = self.rename.take().or(rename);
self.rename_all = self.rename_all.take().or(rename_all);
Expand All @@ -94,6 +97,7 @@ impl StructAttr {
impl_parse! {
StructAttr(input, out) {
"crate" => out.crate_rename = Some(parse_assign_from_str(input)?),
"as" => out.type_as = Some(parse_assign_from_str(input)?),
"type" => out.type_override = Some(parse_assign_str(input)?),
"rename" => out.rename = Some(parse_assign_str(input)?),
"rename_all" => out.rename_all = Some(parse_assign_str(input).and_then(Inflection::try_from)?),
Expand Down
5 changes: 4 additions & 1 deletion macros/src/types/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use syn::{Fields, ItemEnum, Variant};
use crate::{
attr::{EnumAttr, FieldAttr, StructAttr, Tagged, VariantAttr},
deps::Dependencies,
types::{self, type_override},
types::{self, type_as, type_override},
DerivedTS,
};

Expand All @@ -22,6 +22,9 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result<DerivedTS> {
if let Some(attr_type_override) = &enum_attr.type_override {
return type_override::type_override_enum(&enum_attr, &name, attr_type_override);
}
if let Some(attr_type_as) = &enum_attr.type_as {
return type_as::type_as_enum(&enum_attr, &name, attr_type_as);
}

if s.variants.is_empty() {
return Ok(empty_enum(name, enum_attr));
Expand Down
4 changes: 4 additions & 0 deletions macros/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod r#enum;
mod named;
mod newtype;
mod tuple;
mod type_as;
mod type_override;
mod unit;

Expand All @@ -22,6 +23,9 @@ fn type_def(attr: &StructAttr, ident: &Ident, fields: &Fields) -> Result<Derived
if let Some(attr_type_override) = &attr.type_override {
return type_override::type_override_struct(attr, &name, attr_type_override);
}
if let Some(attr_type_as) = &attr.type_as {
return type_as::type_as_struct(attr, &name, attr_type_as);
}

match fields {
Fields::Named(named) => match named.named.len() {
Expand Down
68 changes: 68 additions & 0 deletions macros/src/types/type_as.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use quote::quote;
use syn::{Result, Type};

use crate::{
attr::{EnumAttr, StructAttr},
deps::Dependencies,
DerivedTS,
};

pub(crate) fn type_as_struct(attr: &StructAttr, name: &str, type_as: &Type) -> Result<DerivedTS> {
if attr.rename_all.is_some() {
syn_err!("`rename_all` is not compatible with `as`");
}
if attr.tag.is_some() {
syn_err!("`tag` is not compatible with `as`");
}

let crate_rename = attr.crate_rename();

Ok(DerivedTS {
crate_rename: crate_rename.clone(),
inline: quote!(#type_as::inline()),
inline_flattened: None,
docs: attr.docs.clone(),
dependencies: Dependencies::new(crate_rename),
export: attr.export,
export_to: attr.export_to.clone(),
ts_name: name.to_owned(),
concrete: attr.concrete.clone(),
bound: attr.bound.clone(),
})
}

pub(crate) fn type_as_enum(attr: &EnumAttr, name: &str, type_as: &Type) -> Result<DerivedTS> {
if attr.rename_all.is_some() {
syn_err!("`rename_all` is not compatible with `as`");
}
if attr.rename_all_fields.is_some() {
syn_err!("`rename_all_fields` is not compatible with `as`");
}
if attr.tag.is_some() {
syn_err!("`tag` is not compatible with `as`");
}
if attr.content.is_some() {
syn_err!("`content` is not compatible with `as`");
}
if attr.untagged {
syn_err!("`untagged` is not compatible with `as`");
}
if attr.type_override.is_some() {
syn_err!("`type` is not compatible with `as`");
}

let crate_rename = attr.crate_rename();

Ok(DerivedTS {
crate_rename: crate_rename.clone(),
inline: quote!(#type_as::inline()),
inline_flattened: None,
docs: attr.docs.clone(),
dependencies: Dependencies::new(crate_rename),
export: attr.export,
export_to: attr.export_to.clone(),
ts_name: name.to_owned(),
concrete: attr.concrete.clone(),
bound: attr.bound.clone(),
})
}
3 changes: 3 additions & 0 deletions macros/src/types/type_override.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ pub(crate) fn type_override_enum(
if attr.untagged {
syn_err!("`untagged` is not compatible with `type`");
}
if attr.type_as.is_some() {
syn_err!("`type` is not compatible with `as`");
}

let crate_rename = attr.crate_rename();

Expand Down
5 changes: 5 additions & 0 deletions ts-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ pub mod typelist;
/// Note that you need to add the `export` attribute as well, in order to generate a test which exports the type.
/// <br/><br/>
///
/// - **`#[ts(as = "..")]`**
/// Overrides the type used in Typescript, using the provided Rust type instead.
/// This is useful when you have a custom serializer and deserializer and don't want to implement `TS` manually
/// <br/><br/>
///
/// - **`#[ts(type = "..")]`**
/// Overrides the type used in TypeScript.
/// This is useful when you have a custom serializer and deserializer and don't want to implement `TS` manually
Expand Down
22 changes: 22 additions & 0 deletions ts-rs/tests/top_level_type_as.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use ts_rs::TS;

#[derive(TS)]
#[ts(as = "T")]
pub enum UntaggedEnum<T: TS> {
Left(T),
Right(T),
}

#[test]
pub fn top_level_type_as_enum() {
assert_eq!(UntaggedEnum::<String>::inline(), r#"string"#)
}

#[derive(TS)]
#[ts(as = "T")]
pub struct Wrapper<T: TS>(T);

#[test]
pub fn top_level_type_as_struct() {
assert_eq!(Wrapper::<String>::inline(), r#"string"#)
}

0 comments on commit c862aa6

Please sign in to comment.