From 7e3f169497edbe1f0a4b7f30c2ce6f52127913cb Mon Sep 17 00:00:00 2001 From: Gustavo Date: Thu, 1 Feb 2024 10:33:45 -0300 Subject: [PATCH] Combine #195 with e2e --- e2e/dependencies/consumer/.cargo/config.toml | 2 ++ e2e/dependencies/dependency1/src/lib.rs | 8 ++++- e2e/workspace/.cargo/config.toml | 2 ++ e2e/workspace/crate1/src/lib.rs | 3 +- e2e/workspace/crate2/src/lib.rs | 1 + macros/src/lib.rs | 7 +++++ ts-rs/src/export.rs | 31 ++++++++++++++++++-- ts-rs/src/lib.rs | 10 +++++-- 8 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 e2e/dependencies/consumer/.cargo/config.toml create mode 100644 e2e/workspace/.cargo/config.toml diff --git a/e2e/dependencies/consumer/.cargo/config.toml b/e2e/dependencies/consumer/.cargo/config.toml new file mode 100644 index 000000000..0182382b6 --- /dev/null +++ b/e2e/dependencies/consumer/.cargo/config.toml @@ -0,0 +1,2 @@ +[env] +TS_RS_EXPORT_DIR = { value = "./bindings/", relative = true } diff --git a/e2e/dependencies/dependency1/src/lib.rs b/e2e/dependencies/dependency1/src/lib.rs index aade3eb50..3541e0932 100644 --- a/e2e/dependencies/dependency1/src/lib.rs +++ b/e2e/dependencies/dependency1/src/lib.rs @@ -1,6 +1,12 @@ use ts_rs::TS; #[derive(TS)] +#[ts(export)] pub struct LibraryType { pub a: i32 -} \ No newline at end of file +} + +#[test] +fn env_set() { + assert_ne!(env!("TS_RS_EXPORT_DIR"), ""); +} diff --git a/e2e/workspace/.cargo/config.toml b/e2e/workspace/.cargo/config.toml new file mode 100644 index 000000000..0182382b6 --- /dev/null +++ b/e2e/workspace/.cargo/config.toml @@ -0,0 +1,2 @@ +[env] +TS_RS_EXPORT_DIR = { value = "./bindings/", relative = true } diff --git a/e2e/workspace/crate1/src/lib.rs b/e2e/workspace/crate1/src/lib.rs index 650b721c7..806ee5fbb 100644 --- a/e2e/workspace/crate1/src/lib.rs +++ b/e2e/workspace/crate1/src/lib.rs @@ -1,6 +1,7 @@ use ts_rs::TS; #[derive(TS)] +#[ts(export)] pub struct Crate1 { pub x: i32 -} \ No newline at end of file +} diff --git a/e2e/workspace/crate2/src/lib.rs b/e2e/workspace/crate2/src/lib.rs index c82468175..660a44ba0 100644 --- a/e2e/workspace/crate2/src/lib.rs +++ b/e2e/workspace/crate2/src/lib.rs @@ -1,6 +1,7 @@ use ts_rs::TS; #[derive(TS)] +#[ts(export)] pub struct Crate2 { pub x: i32 } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 9b6639e96..da59c8401 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -47,12 +47,18 @@ impl DerivedTS { } fn into_impl(self, rust_ty: Ident, generics: Generics) -> TokenStream { + let mut get_export_to = quote! {}; let export_to = match &self.export_to { Some(dirname) if dirname.ends_with('/') => { format!("{}{}.ts", dirname, self.name) } Some(filename) => filename.clone(), None => { + get_export_to = quote! { + fn get_export_to() -> Option { + ts_rs::__private::get_export_to_path::() + } + }; format!("bindings/{}.ts", self.name) } }; @@ -84,6 +90,7 @@ impl DerivedTS { quote! { #impl_start { const EXPORT_TO: Option<&'static str> = Some(#export_to); + #get_export_to fn decl() -> String { #decl diff --git a/ts-rs/src/export.rs b/ts-rs/src/export.rs index fdaf85164..523e0fedd 100644 --- a/ts-rs/src/export.rs +++ b/ts-rs/src/export.rs @@ -3,6 +3,7 @@ use std::{ collections::BTreeMap, fmt::Write, path::{Component, Path, PathBuf}, + sync::OnceLock, }; use thiserror::Error; @@ -59,6 +60,29 @@ pub(crate) fn export_type_to>( Ok(()) } + +#[doc(hidden)] +pub mod __private { + use super::*; + + const EXPORT_DIR_ENV_VAR: &str = "TS_RS_EXPORT_DIR"; + fn provided_default_dir() -> &'static Option { + static EXPORT_TO: OnceLock> = OnceLock::new(); + EXPORT_TO.get_or_init(|| std::env::var(EXPORT_DIR_ENV_VAR).ok()) + } + + /// Returns the path to where `T` should be exported using the `TS_RS_EXPORT_DIR` environment variable. + /// + /// This should only be used by the TS derive macro; the `get_export_to` trait method should not + /// be overridden if the `#[ts(export_to = ..)]` attribute exists. + pub fn get_export_to_path() -> Option { + provided_default_dir().as_ref().map_or_else( + || T::EXPORT_TO.map(ToString::to_string), + |path| Some(format!("{path}/{}.ts", T::name())), + ) + } +} + /// Returns the generated defintion for `T`. pub(crate) fn export_type_to_string() -> Result { let mut buffer = String::with_capacity(1024); @@ -72,7 +96,7 @@ pub(crate) fn export_type_to_string() -> Result() -> Result { let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").map_err(|_| ManifestDirNotSet)?; let manifest_dir = Path::new(&manifest_dir); - let path = PathBuf::from(T::EXPORT_TO.ok_or(CannotBeExported)?); + let path = PathBuf::from(T::get_export_to().ok_or(CannotBeExported)?); Ok(manifest_dir.join(path)) } @@ -84,7 +108,8 @@ fn generate_decl(out: &mut String) { /// Push an import statement for all dependencies of `T` fn generate_imports(out: &mut String) -> Result<(), ExportError> { - let path = Path::new(T::EXPORT_TO.ok_or(ExportError::CannotBeExported)?); + let export_to = T::get_export_to().ok_or(CannotBeExported)?; + let path = Path::new(&export_to); let deps = T::dependencies(); let deduplicated_deps = deps @@ -94,7 +119,7 @@ fn generate_imports(out: &mut String) -> Result<(), Ex .collect::>(); for (_, dep) in deduplicated_deps { - let rel_path = import_path(path, Path::new(dep.exported_to)); + let rel_path = import_path(path, Path::new(&dep.exported_to)); writeln!( out, "import type {{ {} }} from {:?};", diff --git a/ts-rs/src/lib.rs b/ts-rs/src/lib.rs index d3a1f41ab..f0cf68fb9 100644 --- a/ts-rs/src/lib.rs +++ b/ts-rs/src/lib.rs @@ -167,7 +167,7 @@ use std::{ pub use ts_rs_macros::TS; -pub use crate::export::ExportError; +pub use crate::export::{ExportError, __private}; #[cfg(feature = "chrono-impl")] mod chrono; @@ -263,6 +263,10 @@ mod export; pub trait TS { const EXPORT_TO: Option<&'static str> = None; + fn get_export_to() -> Option { + Self::EXPORT_TO.map(ToString::to_string) + } + /// Declaration of this type, e.g. `interface User { user_id: number, ... }`. /// This function will panic if the type has no declaration. fn decl() -> String { @@ -342,7 +346,7 @@ pub struct Dependency { pub ts_name: String, /// Path to where the type would be exported. By default a filename is derived from the types /// name, which can be customized with `#[ts(export_to = "..")]`. - pub exported_to: &'static str, + pub exported_to: String, } impl Dependency { @@ -350,7 +354,7 @@ impl Dependency { /// If `T` is not exportable (meaning `T::EXPORT_TO` is `None`), this function will return /// `None` pub fn from_ty() -> Option { - let exported_to = T::EXPORT_TO?; + let exported_to = T::get_export_to()?; Some(Dependency { type_id: TypeId::of::(), ts_name: T::name(),