diff --git a/CHANGELOG.md b/CHANGELOG.md index 091cd7b27..e474cd17e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ # master +### Breaking +### Features +- Add `#[ts(crate = "..")]` to allow usage of `#[derive(TS)]` from other proc-macro crates ([#274](https://github.com/Aleph-Alpha/ts-rs/pull/274)) +### Fixes + +# v8.0.0 ### Breaking diff --git a/e2e/workspace/Cargo.toml b/e2e/workspace/Cargo.toml index d06ca9311..471e90b02 100644 --- a/e2e/workspace/Cargo.toml +++ b/e2e/workspace/Cargo.toml @@ -1,2 +1,3 @@ [workspace] -members = ["crate1", "crate2", "parent"] \ No newline at end of file +members = ["crate1", "crate2", "parent", "renamed"] +resolver = "2" diff --git a/e2e/workspace/renamed/Cargo.toml b/e2e/workspace/renamed/Cargo.toml new file mode 100644 index 000000000..aad7856cd --- /dev/null +++ b/e2e/workspace/renamed/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "renamed" +version = "0.1.0" +edition = "2021" + +[dependencies] +ts-renamed = { package = "ts-rs", path = "../../../ts-rs" } diff --git a/e2e/workspace/renamed/src/main.rs b/e2e/workspace/renamed/src/main.rs new file mode 100644 index 000000000..fa6cf12e5 --- /dev/null +++ b/e2e/workspace/renamed/src/main.rs @@ -0,0 +1,12 @@ +use ts_renamed::TS; + +#[derive(TS)] +#[ts(crate = "ts_renamed", export)] +pub struct SimpleStruct { + hello: String, + world: u32, +} + +fn main() { + println!("Hello, world!"); +} diff --git a/macros/src/attr/enum.rs b/macros/src/attr/enum.rs index 232d61fe0..e6e4fdcaf 100644 --- a/macros/src/attr/enum.rs +++ b/macros/src/attr/enum.rs @@ -1,16 +1,16 @@ use std::collections::HashMap; -use syn::{Attribute, Ident, Result, Type, WherePredicate}; +use syn::{parse_quote, Attribute, Ident, Path, Result, Type, WherePredicate}; +use super::{parse_assign_from_str, parse_bound}; use crate::{ attr::{parse_assign_inflection, parse_assign_str, parse_concrete, Inflection}, utils::{parse_attrs, parse_docs}, }; -use super::parse_bound; - #[derive(Default)] pub struct EnumAttr { + crate_rename: Option, pub rename_all: Option, pub rename_all_fields: Option, pub rename: Option, @@ -61,9 +61,16 @@ impl EnumAttr { Ok(result) } + pub fn crate_rename(&self) -> Path { + self.crate_rename + .clone() + .unwrap_or_else(|| parse_quote!(::ts_rs)) + } + fn merge( &mut self, EnumAttr { + crate_rename, rename_all, rename_all_fields, rename, @@ -77,6 +84,7 @@ impl EnumAttr { bound, }: EnumAttr, ) { + self.crate_rename = self.crate_rename.take().or(crate_rename); self.rename = self.rename.take().or(rename); self.rename_all = self.rename_all.take().or(rename_all); self.rename_all_fields = self.rename_all_fields.take().or(rename_all_fields); @@ -87,15 +95,21 @@ impl EnumAttr { self.export_to = self.export_to.take().or(export_to); self.docs = docs; self.concrete.extend(concrete); - self.bound = self.bound + self.bound = self + .bound .take() - .map(|b| b.into_iter().chain(bound.clone().unwrap_or_default()).collect()) + .map(|b| { + b.into_iter() + .chain(bound.clone().unwrap_or_default()) + .collect() + }) .or(bound); } } impl_parse! { EnumAttr(input, out) { + "crate" => out.crate_rename = Some(parse_assign_from_str(input)?), "rename" => out.rename = Some(parse_assign_str(input)?), "rename_all" => out.rename_all = Some(parse_assign_inflection(input)?), "rename_all_fields" => out.rename_all_fields = Some(parse_assign_inflection(input)?), diff --git a/macros/src/attr/mod.rs b/macros/src/attr/mod.rs index fcf9fbd69..b80ea7bba 100644 --- a/macros/src/attr/mod.rs +++ b/macros/src/attr/mod.rs @@ -1,11 +1,12 @@ -use std::{collections::HashMap, convert::TryFrom}; +use std::collections::HashMap; pub use field::*; pub use r#enum::*; pub use r#struct::*; use syn::{ parse::{Parse, ParseStream}, - Error, Lit, Result, Token, WherePredicate, punctuated::Punctuated, + punctuated::Punctuated, + Error, Lit, Result, Token, WherePredicate, }; pub use variant::*; @@ -136,7 +137,7 @@ fn parse_bound(input: ParseStream) -> Result> { let parser = Punctuated::::parse_terminated; Ok(string.parse_with(parser)?.into_iter().collect()) - }, + } other => Err(Error::new(other.span(), "expected string")), } } diff --git a/macros/src/attr/struct.rs b/macros/src/attr/struct.rs index 6ed3bf918..ce58a0e5f 100644 --- a/macros/src/attr/struct.rs +++ b/macros/src/attr/struct.rs @@ -1,16 +1,17 @@ -use std::{collections::HashMap, convert::TryFrom}; +use std::collections::HashMap; -use syn::{Attribute, Ident, Result, Type, WherePredicate}; +use syn::{parse_quote, Attribute, Ident, Path, Result, Type, WherePredicate}; +use super::{parse_assign_from_str, parse_bound, parse_concrete}; +use crate::attr::EnumAttr; use crate::{ - attr::{parse_assign_str, parse_concrete, Inflection, VariantAttr}, + attr::{parse_assign_str, Inflection, VariantAttr}, utils::{parse_attrs, parse_docs}, }; -use super::parse_bound; - #[derive(Default, Clone)] pub struct StructAttr { + crate_rename: Option, pub rename_all: Option, pub rename: Option, pub export_to: Option, @@ -38,9 +39,26 @@ impl StructAttr { Ok(result) } + pub fn from_variant(enum_attr: &EnumAttr, variant_attr: &VariantAttr) -> Self { + Self { + crate_rename: Some(enum_attr.crate_rename()), + rename: variant_attr.rename.clone(), + rename_all: variant_attr.rename_all, + // inline and skip are not supported on StructAttr + ..Self::default() + } + } + + pub fn crate_rename(&self) -> Path { + self.crate_rename + .clone() + .unwrap_or_else(|| parse_quote!(::ts_rs)) + } + fn merge( &mut self, StructAttr { + crate_rename, rename_all, rename, export, @@ -51,6 +69,7 @@ impl StructAttr { bound, }: StructAttr, ) { + self.crate_rename = self.crate_rename.take().or(crate_rename); self.rename = self.rename.take().or(rename); self.rename_all = self.rename_all.take().or(rename_all); self.export_to = self.export_to.take().or(export_to); @@ -58,30 +77,21 @@ impl StructAttr { self.tag = self.tag.take().or(tag); self.docs = docs; self.concrete.extend(concrete); - self.bound = self.bound + self.bound = self + .bound .take() - .map(|b| b.into_iter().chain(bound.clone().unwrap_or_default()).collect()) + .map(|b| { + b.into_iter() + .chain(bound.clone().unwrap_or_default()) + .collect() + }) .or(bound); } } -impl From for StructAttr { - fn from( - VariantAttr { - rename, rename_all, .. - }: VariantAttr, - ) -> Self { - Self { - rename, - rename_all, - // inline and skip are not supported on StructAttr - ..Self::default() - } - } -} - impl_parse! { StructAttr(input, out) { + "crate" => out.crate_rename = Some(parse_assign_from_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)?), "tag" => out.tag = Some(parse_assign_str(input)?), diff --git a/macros/src/deps.rs b/macros/src/deps.rs index 630fda75f..b99c49fce 100644 --- a/macros/src/deps.rs +++ b/macros/src/deps.rs @@ -1,26 +1,35 @@ use proc_macro2::TokenStream; use quote::{quote, ToTokens}; -use syn::Type; +use syn::{Path, Type}; -#[derive(Default)] pub struct Dependencies { dependencies: Vec, + crate_rename: Path, pub types: Vec, } impl Dependencies { + pub fn new(crate_rename: Path) -> Self { + Self { + dependencies: Vec::default(), + crate_rename, + types: Vec::default(), + } + } /// Adds all dependencies from the given type pub fn append_from(&mut self, ty: &Type) { + let crate_rename = &self.crate_rename; self.dependencies - .push(quote![.extend(<#ty as ::ts_rs::TS>::dependency_types())]); + .push(quote![.extend(<#ty as #crate_rename::TS>::dependency_types())]); self.types.push(ty.clone()); } /// Adds the given type. pub fn push(&mut self, ty: &Type) { + let crate_rename = &self.crate_rename; self.dependencies.push(quote![.push::<#ty>()]); self.dependencies.push(quote![ - .extend(<#ty as ::ts_rs::TS>::generics()) + .extend(<#ty as #crate_rename::TS>::generics()) ]); self.types.push(ty.clone()); } @@ -33,9 +42,10 @@ impl Dependencies { impl ToTokens for Dependencies { fn to_tokens(&self, tokens: &mut TokenStream) { + let crate_rename = &self.crate_rename; let lines = &self.dependencies; tokens.extend(quote![{ - use ::ts_rs::typelist::TypeList; + use #crate_rename::typelist::TypeList; ()#(#lines)* }]) } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index c61e8b014..5370e4657 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -6,8 +6,8 @@ use std::collections::{HashMap, HashSet}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use syn::{ - parse_quote, spanned::Spanned, ConstParam, GenericParam, Generics, Item, LifetimeParam, Result, - Type, TypeArray, TypeParam, TypeParen, TypePath, TypeReference, TypeSlice, TypeTuple, + parse_quote, spanned::Spanned, ConstParam, GenericParam, Generics, Item, LifetimeParam, Path, + Result, Type, TypeArray, TypeParam, TypeParen, TypePath, TypeReference, TypeSlice, TypeTuple, WhereClause, WherePredicate, }; @@ -20,6 +20,7 @@ mod deps; mod types; struct DerivedTS { + crate_rename: Path, ts_name: String, docs: String, inline: TokenStream, @@ -59,14 +60,17 @@ impl DerivedTS { docs => Some(quote!(const DOCS: Option<&'static str> = Some(#docs);)), }; + let crate_rename = self.crate_rename.clone(); + let ident = self.ts_name.clone(); let impl_start = generate_impl_block_header( + &crate_rename, &rust_ty, &generics, self.bound.as_deref(), &self.dependencies, ); - let assoc_type = generate_assoc_type(&rust_ty, &generics, &self.concrete); + let assoc_type = generate_assoc_type(&rust_ty, &crate_rename, &generics, &self.concrete); let name = self.generate_name_fn(&generics); let inline = self.generate_inline_fn(); let decl = self.generate_decl_fn(&rust_ty, &generics); @@ -89,7 +93,7 @@ impl DerivedTS { #output_path_fn #[allow(clippy::unused_unit)] - fn dependency_types() -> impl ::ts_rs::typelist::TypeList + fn dependency_types() -> impl #crate_rename::typelist::TypeList where Self: 'static, { @@ -105,11 +109,12 @@ impl DerivedTS { /// parameters. fn name_with_generics(&self, generics: &Generics) -> TokenStream { let name = &self.ts_name; + let crate_rename = &self.crate_rename; let mut generics_ts_names = generics .type_params() .filter(|ty| !self.concrete.contains_key(&ty.ident)) .map(|ty| &ty.ident) - .map(|generic| quote!(<#generic as ::ts_rs::TS>::name())) + .map(|generic| quote!(<#generic as #crate_rename::TS>::name())) .peekable(); if generics_ts_names.peek().is_some() { @@ -166,13 +171,14 @@ impl DerivedTS { "export_bindings_{}", rust_ty.to_string().to_lowercase().replace("r#", "") ); + let crate_rename = &self.crate_rename; let generic_params = generics .type_params() .map(|ty| match self.concrete.get(&ty.ident) { - None => quote! { ::ts_rs::Dummy }, + None => quote! { #crate_rename::Dummy }, Some(ty) => quote! { #ty }, }); - let ty = quote!(<#rust_ty<#(#generic_params),*> as ::ts_rs::TS>); + let ty = quote!(<#rust_ty<#(#generic_params),*> as #crate_rename::TS>); quote! { #[cfg(test)] @@ -184,17 +190,18 @@ impl DerivedTS { } fn generate_generics_fn(&self, generics: &Generics) -> TokenStream { + let crate_rename = &self.crate_rename; let generics = generics .type_params() .filter(|ty| !self.concrete.contains_key(&ty.ident)) - .map(|TypeParam { ident, .. }| quote![.push::<#ident>().extend(<#ident as ::ts_rs::TS>::generics())]); + .map(|TypeParam { ident, .. }| quote![.push::<#ident>().extend(<#ident as #crate_rename::TS>::generics())]); quote! { #[allow(clippy::unused_unit)] - fn generics() -> impl ::ts_rs::typelist::TypeList + fn generics() -> impl #crate_rename::typelist::TypeList where Self: 'static, { - use ::ts_rs::typelist::TypeList; + use #crate_rename::typelist::TypeList; ()#(#generics)* } } @@ -245,8 +252,14 @@ impl DerivedTS { /// them with the dummy types generated by `generate_generic_types()`. fn generate_decl_fn(&mut self, rust_ty: &Ident, generics: &Generics) -> TokenStream { let name = &self.ts_name; + let crate_rename = &self.crate_rename; let generic_types = self.generate_generic_types(generics); - let ts_generics = format_generics(&mut self.dependencies, generics, &self.concrete); + let ts_generics = format_generics( + &mut self.dependencies, + crate_rename, + generics, + &self.concrete, + ); use GenericParam as G; // These are the generic parameters we'll be using. @@ -270,7 +283,7 @@ impl DerivedTS { } fn decl() -> String { #generic_types - let inline = <#rust_ty<#(#generic_idents,)*> as ::ts_rs::TS>::inline(); + let inline = <#rust_ty<#(#generic_idents,)*> as #crate_rename::TS>::inline(); let generics = #ts_generics; format!("type {}{generics} = {inline};", #name) } @@ -280,6 +293,7 @@ impl DerivedTS { fn generate_assoc_type( rust_ty: &Ident, + crate_rename: &Path, generics: &Generics, concrete: &HashMap, ) -> TokenStream { @@ -287,7 +301,7 @@ fn generate_assoc_type( let generics_params = generics.params.iter().map(|x| match x { G::Type(ty) => match concrete.get(&ty.ident) { - None => quote! { ::ts_rs::Dummy }, + None => quote! { #crate_rename::Dummy }, Some(ty) => quote! { #ty }, }, G::Const(ConstParam { ident, .. }) => quote! { #ident }, @@ -299,6 +313,7 @@ fn generate_assoc_type( // generate start of the `impl TS for #ty` block, up to (excluding) the open brace fn generate_impl_block_header( + crate_rename: &Path, ty: &Ident, generics: &Generics, bounds: Option<&[WherePredicate]>, @@ -335,15 +350,19 @@ fn generate_impl_block_header( let where_bound = match bounds { Some(bounds) => quote! { where #(#bounds),* }, None => { - let bounds = generate_where_clause(generics, dependencies); + let bounds = generate_where_clause(crate_rename, generics, dependencies); quote! { #bounds } } }; - quote!(impl <#(#params),*> ::ts_rs::TS for #ty <#(#type_args),*> #where_bound) + quote!(impl <#(#params),*> #crate_rename::TS for #ty <#(#type_args),*> #where_bound) } -fn generate_where_clause(generics: &Generics, dependencies: &Dependencies) -> WhereClause { +fn generate_where_clause( + crate_rename: &Path, + generics: &Generics, + dependencies: &Dependencies, +) -> WhereClause { let used_types = { let is_type_param = |id: &Ident| generics.type_params().any(|p| &p.ident == id); @@ -356,7 +375,7 @@ fn generate_where_clause(generics: &Generics, dependencies: &Dependencies) -> Wh let existing = generics.where_clause.iter().flat_map(|w| &w.predicates); parse_quote! { - where #(#existing,)* #(#used_types: ::ts_rs::TS),* + where #(#existing,)* #(#used_types: #crate_rename::TS),* } } diff --git a/macros/src/types/enum.rs b/macros/src/types/enum.rs index f35cb9863..130b4aaba 100644 --- a/macros/src/types/enum.rs +++ b/macros/src/types/enum.rs @@ -11,6 +11,8 @@ use crate::{ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { let enum_attr: EnumAttr = EnumAttr::from_attrs(&s.attrs)?; + let crate_rename = enum_attr.crate_rename(); + let name = match &enum_attr.rename { Some(existing) => existing.clone(), None => s.ident.to_string(), @@ -22,11 +24,12 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { if s.variants.is_empty() { return Ok(DerivedTS { + crate_rename: crate_rename.clone(), ts_name: name, docs: enum_attr.docs, inline: quote!("never".to_owned()), inline_flattened: None, - dependencies: Dependencies::default(), + dependencies: Dependencies::new(crate_rename), export: enum_attr.export, export_to: enum_attr.export_to, concrete: enum_attr.concrete, @@ -35,7 +38,7 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { } let mut formatted_variants = Vec::new(); - let mut dependencies = Dependencies::default(); + let mut dependencies = Dependencies::new(crate_rename.clone()); for variant in &s.variants { format_variant( &mut formatted_variants, @@ -46,6 +49,7 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { } Ok(DerivedTS { + crate_rename, inline: quote!([#(#formatted_variants),*].join(" | ")), inline_flattened: Some(quote!( format!("({})", [#(#formatted_variants),*].join(" | ")) @@ -66,6 +70,7 @@ fn format_variant( enum_attr: &EnumAttr, variant: &Variant, ) -> syn::Result<()> { + let crate_rename = enum_attr.crate_rename(); let variant_attr = VariantAttr::new(&variant.attrs, enum_attr)?; if variant_attr.skip { @@ -79,8 +84,9 @@ fn format_variant( (None, Some(rn)) => rn.apply(&variant.ident.to_string()), }; + let struct_attr = StructAttr::from_variant(enum_attr, &variant_attr); let variant_type = types::type_def( - &StructAttr::from(variant_attr), + &struct_attr, // since we are generating the variant as a struct, it doesn't have a name &format_ident!("_"), &variant.fields, @@ -120,11 +126,11 @@ fn format_variant( } (Some(type_override), None) => quote! { #type_override }, (None, Some(type_as)) => { - quote!(<#type_as as ::ts_rs::TS>::name()) + quote!(<#type_as as #crate_rename::TS>::name()) } (None, None) => { let ty = &unnamed.unnamed[0].ty; - quote!(<#ty as ::ts_rs::TS>::name()) + quote!(<#ty as #crate_rename::TS>::name()) } }; @@ -171,11 +177,11 @@ fn format_variant( } (Some(type_override), None) => quote! { #type_override }, (None, Some(type_as)) => { - quote!(<#type_as as ::ts_rs::TS>::name()) + quote!(<#type_as as #crate_rename::TS>::name()) } (None, None) => { let ty = &unnamed.unnamed[0].ty; - quote!(<#ty as ::ts_rs::TS>::name()) + quote!(<#ty as #crate_rename::TS>::name()) } }; @@ -198,11 +204,13 @@ fn format_variant( // bindings for an empty enum (`never` in TS) fn empty_enum(name: impl Into, enum_attr: EnumAttr) -> DerivedTS { let name = name.into(); + let crate_rename = enum_attr.crate_rename(); DerivedTS { + crate_rename: crate_rename.clone(), inline: quote!("never".to_owned()), docs: enum_attr.docs, inline_flattened: None, - dependencies: Dependencies::default(), + dependencies: Dependencies::new(crate_rename), export: enum_attr.export, export_to: enum_attr.export_to, ts_name: name, diff --git a/macros/src/types/named.rs b/macros/src/types/named.rs index bd86ab6a4..c84fa6c1c 100644 --- a/macros/src/types/named.rs +++ b/macros/src/types/named.rs @@ -1,6 +1,8 @@ use proc_macro2::TokenStream; -use quote::{quote}; -use syn::{spanned::Spanned, Field, FieldsNamed, GenericArgument, PathArguments, Result, Type}; +use quote::quote; +use syn::{ + spanned::Spanned, Field, FieldsNamed, GenericArgument, Path, PathArguments, Result, Type, +}; use crate::{ attr::{FieldAttr, Inflection, Optional, StructAttr}, @@ -10,9 +12,11 @@ use crate::{ }; pub(crate) fn named(attr: &StructAttr, name: &str, fields: &FieldsNamed) -> Result { + let crate_rename = attr.crate_rename(); + let mut formatted_fields = Vec::new(); let mut flattened_fields = Vec::new(); - let mut dependencies = Dependencies::default(); + let mut dependencies = Dependencies::new(crate_rename.clone()); if let Some(tag) = &attr.tag { let formatted = format!("{}: \"{}\",", tag, name); @@ -23,6 +27,7 @@ pub(crate) fn named(attr: &StructAttr, name: &str, fields: &FieldsNamed) -> Resu for field in &fields.named { format_field( + &crate_rename, &mut formatted_fields, &mut flattened_fields, &mut dependencies, @@ -43,6 +48,7 @@ pub(crate) fn named(attr: &StructAttr, name: &str, fields: &FieldsNamed) -> Resu }; Ok(DerivedTS { + crate_rename, // the `replace` combines `{ ... } & { ... }` into just one `{ ... }`. Not necessary, but it // results in simpler type definitions. inline: quote!(#inline.replace(" } & { ", " ")), @@ -68,6 +74,7 @@ pub(crate) fn named(attr: &StructAttr, name: &str, fields: &FieldsNamed) -> Resu // but for enums is // ({ /* variant data */ } | { /* variant data */ }) fn format_field( + crate_rename: &Path, formatted_fields: &mut Vec, flattened_fields: &mut Vec, dependencies: &mut Dependencies, @@ -124,7 +131,7 @@ fn format_field( _ => {} } - flattened_fields.push(quote!(<#ty as ::ts_rs::TS>::inline_flattened())); + flattened_fields.push(quote!(<#ty as #crate_rename::TS>::inline_flattened())); dependencies.append_from(ty); return Ok(()); } @@ -132,10 +139,10 @@ fn format_field( let formatted_ty = type_override.map(|t| quote!(#t)).unwrap_or_else(|| { if inline { dependencies.append_from(ty); - quote!(<#ty as ::ts_rs::TS>::inline()) + quote!(<#ty as #crate_rename::TS>::inline()) } else { dependencies.push(ty); - quote!(<#ty as ::ts_rs::TS>::name()) + quote!(<#ty as #crate_rename::TS>::name()) } }); let field_name = to_ts_ident(field.ident.as_ref().unwrap()); diff --git a/macros/src/types/newtype.rs b/macros/src/types/newtype.rs index 759ebec8a..df198da5c 100644 --- a/macros/src/types/newtype.rs +++ b/macros/src/types/newtype.rs @@ -1,4 +1,4 @@ -use quote::{quote}; +use quote::quote; use syn::{FieldsUnnamed, Result}; use crate::{ @@ -26,6 +26,8 @@ pub(crate) fn newtype(attr: &StructAttr, name: &str, fields: &FieldsUnnamed) -> docs: _, } = FieldAttr::from_attrs(&inner.attrs)?; + let crate_rename = attr.crate_rename(); + match (&rename_inner, skip, optional.optional, flatten) { (Some(_), ..) => syn_err_spanned!(fields; "`rename` is not applicable to newtype fields"), (_, true, ..) => return super::unit::null(attr, name), @@ -44,7 +46,7 @@ pub(crate) fn newtype(attr: &StructAttr, name: &str, fields: &FieldsUnnamed) -> let inner_ty = type_as.as_ref().unwrap_or(&inner.ty).clone(); - let mut dependencies = Dependencies::default(); + let mut dependencies = Dependencies::new(crate_rename.clone()); match (type_override.is_none(), inline) { (false, _) => (), @@ -54,11 +56,12 @@ pub(crate) fn newtype(attr: &StructAttr, name: &str, fields: &FieldsUnnamed) -> let inline_def = match type_override { Some(ref o) => quote!(#o.to_owned()), - None if inline => quote!(<#inner_ty as ::ts_rs::TS>::inline()), - None => quote!(<#inner_ty as ::ts_rs::TS>::name()), + None if inline => quote!(<#inner_ty as #crate_rename::TS>::inline()), + None => quote!(<#inner_ty as #crate_rename::TS>::name()), }; Ok(DerivedTS { + crate_rename, inline: inline_def, inline_flattened: None, docs: attr.docs.clone(), diff --git a/macros/src/types/tuple.rs b/macros/src/types/tuple.rs index 4aa0f408d..e85ca1a39 100644 --- a/macros/src/types/tuple.rs +++ b/macros/src/types/tuple.rs @@ -1,6 +1,6 @@ use proc_macro2::TokenStream; -use quote::{quote}; -use syn::{Field, FieldsUnnamed, Result}; +use quote::quote; +use syn::{Field, FieldsUnnamed, Path, Result}; use crate::{ attr::{FieldAttr, StructAttr}, @@ -16,13 +16,20 @@ pub(crate) fn tuple(attr: &StructAttr, name: &str, fields: &FieldsUnnamed) -> Re syn_err!("`tag` is not applicable to tuple structs"); } + let crate_rename = attr.crate_rename(); let mut formatted_fields = Vec::new(); - let mut dependencies = Dependencies::default(); + let mut dependencies = Dependencies::new(crate_rename.clone()); for field in &fields.unnamed { - format_field(&mut formatted_fields, &mut dependencies, field)?; + format_field( + &crate_rename, + &mut formatted_fields, + &mut dependencies, + field, + )?; } Ok(DerivedTS { + crate_rename, inline: quote! { format!( "[{}]", @@ -41,6 +48,7 @@ pub(crate) fn tuple(attr: &StructAttr, name: &str, fields: &FieldsUnnamed) -> Re } fn format_field( + crate_rename: &Path, formatted_fields: &mut Vec, dependencies: &mut Dependencies, field: &Field, @@ -80,8 +88,8 @@ fn format_field( formatted_fields.push(match type_override { Some(ref o) => quote!(#o.to_owned()), - None if inline => quote!(<#ty as ::ts_rs::TS>::inline()), - None => quote!(<#ty as ::ts_rs::TS>::name()), + None if inline => quote!(<#ty as #crate_rename::TS>::inline()), + None => quote!(<#ty as #crate_rename::TS>::name()), }); match (inline, type_override) { diff --git a/macros/src/types/unit.rs b/macros/src/types/unit.rs index 88cad8819..6a3c0c36f 100644 --- a/macros/src/types/unit.rs +++ b/macros/src/types/unit.rs @@ -5,12 +5,14 @@ use crate::{attr::StructAttr, deps::Dependencies, DerivedTS}; pub(crate) fn empty_object(attr: &StructAttr, name: &str) -> Result { check_attributes(attr)?; + let crate_rename = attr.crate_rename(); Ok(DerivedTS { + crate_rename: crate_rename.clone(), inline: quote!("Record".to_owned()), inline_flattened: None, docs: attr.docs.clone(), - dependencies: Dependencies::default(), + dependencies: Dependencies::new(crate_rename), export: attr.export, export_to: attr.export_to.clone(), ts_name: name.to_owned(), @@ -21,12 +23,14 @@ pub(crate) fn empty_object(attr: &StructAttr, name: &str) -> Result { pub(crate) fn empty_array(attr: &StructAttr, name: &str) -> Result { check_attributes(attr)?; + let crate_rename = attr.crate_rename(); Ok(DerivedTS { + crate_rename: crate_rename.clone(), inline: quote!("never[]".to_owned()), inline_flattened: None, docs: attr.docs.clone(), - dependencies: Dependencies::default(), + dependencies: Dependencies::new(crate_rename), export: attr.export, export_to: attr.export_to.clone(), ts_name: name.to_owned(), @@ -37,12 +41,14 @@ pub(crate) fn empty_array(attr: &StructAttr, name: &str) -> Result { pub(crate) fn null(attr: &StructAttr, name: &str) -> Result { check_attributes(attr)?; + let crate_rename = attr.crate_rename(); Ok(DerivedTS { + crate_rename: crate_rename.clone(), inline: quote!("null".to_owned()), inline_flattened: None, docs: attr.docs.clone(), - dependencies: Dependencies::default(), + dependencies: Dependencies::new(crate_rename), export: attr.export, export_to: attr.export_to.clone(), ts_name: name.to_owned(), diff --git a/macros/src/utils.rs b/macros/src/utils.rs index c7c4beed4..01a99538d 100644 --- a/macros/src/utils.rs +++ b/macros/src/utils.rs @@ -1,10 +1,10 @@ -use std::{collections::HashMap, convert::TryFrom}; +use std::collections::HashMap; use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::{ - spanned::Spanned, Attribute, Error, Expr, ExprLit, GenericParam, Generics, Lit, Meta, Result, - Type, + spanned::Spanned, Attribute, Error, Expr, ExprLit, GenericParam, Generics, Lit, Meta, Path, + Result, Type, }; use crate::deps::Dependencies; @@ -226,6 +226,7 @@ mod warning { /// If a default type arg is encountered, it will be added to the dependencies. pub fn format_generics( deps: &mut Dependencies, + crate_rename: &Path, generics: &Generics, concrete: &HashMap, ) -> TokenStream { @@ -241,7 +242,7 @@ pub fn format_generics( if let Some(default) = &type_param.default { deps.push(default); Some(quote!( - format!("{} = {}", #ty, <#default as ::ts_rs::TS>::name()) + format!("{} = {}", #ty, <#default as #crate_rename::TS>::name()) )) } else { Some(quote!(#ty.to_owned())) diff --git a/ts-rs/src/lib.rs b/ts-rs/src/lib.rs index 69d6ed63c..317b081e3 100644 --- a/ts-rs/src/lib.rs +++ b/ts-rs/src/lib.rs @@ -177,6 +177,10 @@ pub mod typelist; /// ### container attributes /// attributes applicable for both structs and enums /// +/// - **`#[ts(crate = "..")]`** +/// Generates code which references the module passed to it instead of defaulting to `::ts_rs` +/// This is useful for cases where you have to re-export the crate. +/// /// - **`#[ts(export)]`** /// Generates a test which will export the type, by default to `bindings/.ts` when running /// `cargo test`. The default base directory can be overridden with the `TS_RS_EXPORT_DIR` environment variable.