Skip to content

Commit

Permalink
add "as" derive helper attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
josema03 committed Oct 17, 2023
1 parent c4bee20 commit 38cba25
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 17 deletions.
4 changes: 4 additions & 0 deletions macros/src/attr/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::utils::parse_attrs;

#[derive(Default)]
pub struct FieldAttr {
pub type_as: Option<String>,
pub type_override: Option<String>,
pub rename: Option<String>,
pub inline: bool,
Expand All @@ -29,6 +30,7 @@ impl FieldAttr {
fn merge(
&mut self,
FieldAttr {
type_as,
type_override,
rename,
inline,
Expand All @@ -38,6 +40,7 @@ impl FieldAttr {
}: FieldAttr,
) {
self.rename = self.rename.take().or(rename);
self.type_as = self.type_as.take().or(type_as);
self.type_override = self.type_override.take().or(type_override);
self.inline = self.inline || inline;
self.skip = self.skip || skip;
Expand All @@ -48,6 +51,7 @@ impl FieldAttr {

impl_parse! {
FieldAttr(input, out) {
"as" => out.type_as = Some(parse_assign_str(input)?),
"type" => out.type_override = Some(parse_assign_str(input)?),
"rename" => out.rename = Some(parse_assign_str(input)?),
"inline" => out.inline = true,
Expand Down
28 changes: 21 additions & 7 deletions macros/src/types/named.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ fn format_field(
generics: &Generics,
) -> Result<()> {
let FieldAttr {
type_as,
type_override,
rename,
inline,
Expand All @@ -75,16 +76,29 @@ fn format_field(
return Ok(());
}

let (ty, optional_annotation) = match optional {
true => (extract_option_argument(&field.ty)?, "?"),
false => (&field.ty, ""),
if let (Some(_type_as), Some(_type_override)) = (&type_as, &type_override) {
syn_err!("`type` is not compatible with `as`")
}

let parsed_ty = if let Some(_type_as) = &type_as {
syn::parse_str::<Type>(_type_as)?
} else {
field.ty.clone()
};

let (ty, optional_annotation) = {
match optional {
true => (extract_option_argument(&parsed_ty)?, "?"),
false => (&parsed_ty, ""),
}
};

if flatten {
match (&type_override, &rename, inline) {
(Some(_), _, _) => syn_err!("`type` is not compatible with `flatten`"),
(_, Some(_), _) => syn_err!("`rename` is not compatible with `flatten`"),
(_, _, true) => syn_err!("`inline` is not compatible with `flatten`"),
match (&type_as, &type_override, &rename, inline) {
(Some(_), _, _, _) => syn_err!("`remote` is not compatible with `flatten`"),
(_, Some(_), _, _) => syn_err!("`type` is not compatible with `flatten`"),
(_, _, Some(_), _) => syn_err!("`rename` is not compatible with `flatten`"),
(_, _, _, true) => syn_err!("`inline` is not compatible with `flatten`"),
_ => {}
}

Expand Down
20 changes: 15 additions & 5 deletions macros/src/types/newtype.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use quote::quote;
use syn::{FieldsUnnamed, Generics, Result};
use syn::{FieldsUnnamed, Generics, Result, Type};

use crate::{
attr::{FieldAttr, StructAttr},
Expand All @@ -22,6 +22,7 @@ pub(crate) fn newtype(
}
let inner = fields.unnamed.first().unwrap();
let FieldAttr {
type_as,
type_override,
rename: rename_inner,
inline,
Expand All @@ -38,17 +39,26 @@ pub(crate) fn newtype(
_ => {}
};

let inner_ty = &inner.ty;
if let (Some(_type_as), Some(_type_override)) = (&type_as, &type_override) {
syn_err!("`type` is not compatible with `as`")
}

let inner_ty = if let Some(_type_as) = &type_as {
syn::parse_str::<Type>(_type_as)?
} else {
inner.ty.clone()
};

let mut dependencies = Dependencies::default();
match (inline, &type_override) {
(_, Some(_)) => (),
(true, _) => dependencies.append_from(inner_ty),
(false, _) => dependencies.push_or_append_from(inner_ty),
(true, _) => dependencies.append_from(&inner_ty),
(false, _) => dependencies.push_or_append_from(&inner_ty),
};
let inline_def = match &type_override {
Some(o) => quote!(#o.to_owned()),
None if inline => quote!(<#inner_ty as ts_rs::TS>::inline()),
None => format_type(inner_ty, &mut dependencies, generics),
None => format_type(&inner_ty, &mut dependencies, generics),
};

let generic_args = format_generics(&mut dependencies, generics);
Expand Down
19 changes: 14 additions & 5 deletions macros/src/types/tuple.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Field, FieldsUnnamed, Generics, Result};
use syn::{Field, FieldsUnnamed, Generics, Result, Type};

use crate::{
attr::{FieldAttr, StructAttr},
Expand Down Expand Up @@ -58,8 +58,8 @@ fn format_field(
field: &Field,
generics: &Generics,
) -> Result<()> {
let ty = &field.ty;
let FieldAttr {
type_as,
type_override,
rename,
inline,
Expand All @@ -71,6 +71,15 @@ fn format_field(
if skip {
return Ok(());
}

let ty = if let Some(_type_as) = &type_as {
syn::parse_str::<Type>(_type_as)?
} else {
field.ty.clone()
};
if let (Some(_type_as), Some(_type_override)) = (&type_as, &type_override) {
syn_err!("`type` is not compatible with `as`")
}
if rename.is_some() {
syn_err!("`rename` is not applicable to tuple structs")
}
Expand All @@ -84,16 +93,16 @@ fn format_field(
formatted_fields.push(match &type_override {
Some(o) => quote!(#o.to_owned()),
None if inline => quote!(<#ty as ts_rs::TS>::inline()),
None => format_type(ty, dependencies, generics),
None => format_type(&ty, dependencies, generics),
});

match (inline, &type_override) {
(_, Some(_)) => (),
(false, _) => {
dependencies.push_or_append_from(ty);
dependencies.push_or_append_from(&ty);
}
(true, _) => {
dependencies.append_from(ty);
dependencies.append_from(&ty);
}
};

Expand Down
47 changes: 47 additions & 0 deletions ts-rs/tests/type_as.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#![allow(dead_code)]

use std::time::Instant;

use ts_rs::TS;

#[derive(TS)]
struct ExternalTypeDef {
a: i32,
b: i32,
c: i32,
}

#[test]
fn struct_properties() {
#[derive(TS)]
struct Override {
a: i32,
#[ts(as = "ExternalTypeDef")]
#[ts(inline)]
x: Instant,
}

assert_eq!(
Override::inline(),
"{ a: number, x: { a: number, b: number, c: number, }, }"
)
}

#[test]
fn enum_variants() {
#[derive(TS)]
enum OverrideEnum {
A(#[ts(as = "ExternalTypeDef")] Instant),
B {
#[ts(as = "ExternalTypeDef")]
x: i32,
y: i32,
z: i32,
},
}

assert_eq!(
OverrideEnum::inline(),
r#"{ "A": Remote } | { "B": { x: Remote, y: number, z: number, } }"#
)
}

0 comments on commit 38cba25

Please sign in to comment.