From fe152f60094f4817bf050e3f3d098dbfc2f9345c Mon Sep 17 00:00:00 2001 From: Sammy Harris Date: Sun, 21 Jan 2024 14:43:48 -0500 Subject: [PATCH 01/12] feat: default export path --- macros/src/lib.rs | 11 ++++++++++- ts-rs/src/export.rs | 17 +++++++++++++---- ts-rs/src/lib.rs | 10 +++++++--- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 9b6639e96..9607b4038 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -47,12 +47,21 @@ 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 { + provided_default_path().as_ref().map_or_else( + || Self::EXPORT_TO.map(ToString::to_string), + |path| Some(format!("{path}/{}.ts", Self::name())), + ) + } + }; format!("bindings/{}.ts", self.name) } }; @@ -84,7 +93,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 c4b9345e4..be0a0f2a1 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,8 +60,15 @@ pub(crate) fn export_type_to>( Ok(()) } +pub(crate) const EXPORT_PATH_ENV_VAR: &str = "TS_RS_EXPORT_PATH"; + +pub fn provided_default_path() -> &'static Option { + static EXPORT_TO: OnceLock> = OnceLock::new(); + EXPORT_TO.get_or_init(|| std::env::var(EXPORT_PATH_ENV_VAR).ok()) +} + /// Returns the generated defintion for `T`. -pub(crate) fn export_type_to_string() -> Result { +pub fn export_type_to_string() -> Result { let mut buffer = String::with_capacity(1024); buffer.push_str(NOTE); generate_imports::(&mut buffer)?; @@ -72,7 +80,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 +92,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 +103,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 49391ab47..58e991192 100644 --- a/ts-rs/src/lib.rs +++ b/ts-rs/src/lib.rs @@ -159,7 +159,7 @@ use std::{ pub use ts_rs_macros::TS; -pub use crate::export::ExportError; +pub use crate::export::{provided_default_path, ExportError}; #[cfg(feature = "chrono-impl")] mod chrono; @@ -252,6 +252,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 { @@ -331,7 +335,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 { @@ -339,7 +343,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(), From ac4934d6f8c5f58c0e472d8bbe6628c6e1b7a4a0 Mon Sep 17 00:00:00 2001 From: Sammy Harris Date: Sun, 21 Jan 2024 14:48:48 -0500 Subject: [PATCH 02/12] fix path --- macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 9607b4038..2469f8172 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -56,7 +56,7 @@ impl DerivedTS { None => { get_export_to = quote! { fn get_export_to() -> Option { - provided_default_path().as_ref().map_or_else( + ts_rs::provided_default_path().as_ref().map_or_else( || Self::EXPORT_TO.map(ToString::to_string), |path| Some(format!("{path}/{}.ts", Self::name())), ) From 52e56016ffc87e92f82123b1ec70c280a484d689 Mon Sep 17 00:00:00 2001 From: Sammy Harris Date: Sun, 21 Jan 2024 17:58:02 -0500 Subject: [PATCH 03/12] small refactor w/ some better docs --- macros/src/lib.rs | 5 +---- ts-rs/src/export.rs | 23 +++++++++++++++++++---- ts-rs/src/lib.rs | 2 +- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 2469f8172..90a364194 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -56,10 +56,7 @@ impl DerivedTS { None => { get_export_to = quote! { fn get_export_to() -> Option { - ts_rs::provided_default_path().as_ref().map_or_else( - || Self::EXPORT_TO.map(ToString::to_string), - |path| Some(format!("{path}/{}.ts", Self::name())), - ) + ts_rs::__private::get_export_to_path::() } }; format!("bindings/{}.ts", self.name) diff --git a/ts-rs/src/export.rs b/ts-rs/src/export.rs index be0a0f2a1..68eb0c3b9 100644 --- a/ts-rs/src/export.rs +++ b/ts-rs/src/export.rs @@ -60,11 +60,26 @@ pub(crate) fn export_type_to>( Ok(()) } -pub(crate) const EXPORT_PATH_ENV_VAR: &str = "TS_RS_EXPORT_PATH"; +#[doc(hidden)] +pub mod __private { + use super::*; + + const EXPORT_PATH_ENV_VAR: &str = "TS_RS_EXPORT_PATH"; + fn provided_default_path() -> &'static Option { + static EXPORT_TO: OnceLock> = OnceLock::new(); + EXPORT_TO.get_or_init(|| std::env::var(EXPORT_PATH_ENV_VAR).ok()) + } -pub fn provided_default_path() -> &'static Option { - static EXPORT_TO: OnceLock> = OnceLock::new(); - EXPORT_TO.get_or_init(|| std::env::var(EXPORT_PATH_ENV_VAR).ok()) + /// Returns the path to where `T` should be exported using the `TS_RS_EXPORT_PATH` 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_path().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`. diff --git a/ts-rs/src/lib.rs b/ts-rs/src/lib.rs index 58e991192..ae26ce292 100644 --- a/ts-rs/src/lib.rs +++ b/ts-rs/src/lib.rs @@ -159,7 +159,7 @@ use std::{ pub use ts_rs_macros::TS; -pub use crate::export::{provided_default_path, ExportError}; +pub use crate::export::{ExportError, __private}; #[cfg(feature = "chrono-impl")] mod chrono; From 1c091bf1322316236339677a23828f87725085c6 Mon Sep 17 00:00:00 2001 From: Sammy Harris Date: Sun, 21 Jan 2024 18:01:25 -0500 Subject: [PATCH 04/12] rename env var to *_DIR since it is used as a directory --- ts-rs/src/export.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ts-rs/src/export.rs b/ts-rs/src/export.rs index 68eb0c3b9..636f14319 100644 --- a/ts-rs/src/export.rs +++ b/ts-rs/src/export.rs @@ -64,18 +64,18 @@ pub(crate) fn export_type_to>( pub mod __private { use super::*; - const EXPORT_PATH_ENV_VAR: &str = "TS_RS_EXPORT_PATH"; - fn provided_default_path() -> &'static Option { + 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_PATH_ENV_VAR).ok()) + 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_PATH` environment variable. + /// 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_path().as_ref().map_or_else( + provided_default_dir().as_ref().map_or_else( || T::EXPORT_TO.map(ToString::to_string), |path| Some(format!("{path}/{}.ts", T::name())), ) From 862f386f91a04eaeaac6214f40c9abd1dbeb8bb2 Mon Sep 17 00:00:00 2001 From: escritorio-gustavo <131818645+escritorio-gustavo@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:16:36 -0300 Subject: [PATCH 05/12] Update export.rs to fix a compiler error --- ts-rs/src/export.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts-rs/src/export.rs b/ts-rs/src/export.rs index f22625dcd..a963575c0 100644 --- a/ts-rs/src/export.rs +++ b/ts-rs/src/export.rs @@ -177,7 +177,7 @@ 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::get_export_to().ok_or(CannotBeExported(std::any::type_name::()))?); + let path = Path::new(&T::get_export_to().ok_or(CannotBeExported(std::any::type_name::()))?); let deps = T::dependencies(); let deduplicated_deps = deps From 66a5294dd39308523afa3532b72b96b145ef5c92 Mon Sep 17 00:00:00 2001 From: escritorio-gustavo <131818645+escritorio-gustavo@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:33:04 -0300 Subject: [PATCH 06/12] Update export.rs --- ts-rs/src/export.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ts-rs/src/export.rs b/ts-rs/src/export.rs index a963575c0..313148530 100644 --- a/ts-rs/src/export.rs +++ b/ts-rs/src/export.rs @@ -177,7 +177,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::get_export_to().ok_or(CannotBeExported(std::any::type_name::()))?); + let export_to = T::get_export_to().ok_or(CannotBeExported(std::any::type_name::()))?; + let path = Path::new(&export_to); let deps = T::dependencies(); let deduplicated_deps = deps From da22efa3f7ad6bb3a8d6775d1faa2458dd38d6b0 Mon Sep 17 00:00:00 2001 From: Sammy Harris Date: Tue, 6 Feb 2024 21:29:24 -0600 Subject: [PATCH 07/12] added docs --- ts-rs/src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ts-rs/src/lib.rs b/ts-rs/src/lib.rs index b9ed2df6c..b3c774ac3 100644 --- a/ts-rs/src/lib.rs +++ b/ts-rs/src/lib.rs @@ -199,10 +199,18 @@ pub mod typelist; /// /// - `#[ts(export)]`: /// Generates a test which will export the type, by default to `bindings/.ts` when running -/// `cargo test` +/// `cargo test`. The default base directory can be overridden with the `TS_RS_EXPORT_DIR` environment variable. +/// Adding the variable to a project's [config.toml](https://doc.rust-lang.org/cargo/reference/config.html#env) can +/// make it easier to manage. +/// ```toml +/// # /.cargo/config.toml +/// [env] +/// TS_RS_EXPORT_DIR = { value = "", relative = true } +/// ``` /// /// - `#[ts(export_to = "..")]`: /// Specifies where the type should be exported to. Defaults to `bindings/.ts`. +/// The `export_to` attribute will also override the `TS_RS_EXPORT_DIR` environment variable. /// If the provided path ends in a trailing `/`, it is interpreted as a directory. /// Note that you need to add the `export` attribute as well, in order to generate a test which exports the type. /// From 55d974361c4125ec9b57f1480c1ab5ae292431c6 Mon Sep 17 00:00:00 2001 From: Sammy Harris Date: Tue, 6 Feb 2024 21:37:51 -0600 Subject: [PATCH 08/12] undo accidental visibility change --- ts-rs/src/export.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ts-rs/src/export.rs b/ts-rs/src/export.rs index 313148530..fe03bcf16 100644 --- a/ts-rs/src/export.rs +++ b/ts-rs/src/export.rs @@ -1,3 +1,4 @@ +use std::sync::Mutex; use std::{ any::TypeId, collections::BTreeMap, @@ -5,7 +6,6 @@ use std::{ path::{Component, Path, PathBuf}, sync::OnceLock, }; -use std::sync::Mutex; use thiserror::Error; @@ -153,7 +153,7 @@ pub mod __private { } /// Returns the generated defintion for `T`. -pub fn export_type_to_string() -> Result { +pub(crate) fn export_type_to_string() -> Result { let mut buffer = String::with_capacity(1024); buffer.push_str(NOTE); generate_imports::(&mut buffer)?; @@ -165,7 +165,8 @@ pub 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::get_export_to().ok_or(CannotBeExported(std::any::type_name::()))?); + let path = + PathBuf::from(T::get_export_to().ok_or(CannotBeExported(std::any::type_name::()))?); Ok(manifest_dir.join(path)) } From 826c23053f94d500cad66efa5e183b39614de7e4 Mon Sep 17 00:00:00 2001 From: Sammy Harris Date: Sun, 11 Feb 2024 11:07:58 -0600 Subject: [PATCH 09/12] document why __private mod is pub --- ts-rs/src/export.rs | 2 +- ts-rs/src/lib.rs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ts-rs/src/export.rs b/ts-rs/src/export.rs index f5139376b..1ae53f1ed 100644 --- a/ts-rs/src/export.rs +++ b/ts-rs/src/export.rs @@ -3,8 +3,8 @@ use std::{ collections::BTreeMap, fmt::Write, path::{Component, Path, PathBuf}, - sync::OnceLock, sync::Mutex, + sync::OnceLock, }; use thiserror::Error; diff --git a/ts-rs/src/lib.rs b/ts-rs/src/lib.rs index ab1ce5040..ef113a75a 100644 --- a/ts-rs/src/lib.rs +++ b/ts-rs/src/lib.rs @@ -168,9 +168,13 @@ use std::{ pub use ts_rs_macros::TS; -pub use crate::export::{ExportError, __private}; +pub use crate::export::ExportError; use crate::typelist::TypeList; +// Used in generated code. Not public API +#[doc(hidden)] +pub use crate::export::__private; + #[cfg(feature = "chrono-impl")] mod chrono; mod export; From b5b922d18df68335279544d7dc40248698463eab Mon Sep 17 00:00:00 2001 From: Sammy Harris Date: Sun, 11 Feb 2024 11:25:06 -0600 Subject: [PATCH 10/12] add default env dir test to ci --- .github/workflows/test.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4011b70ae..94457a024 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,11 @@ jobs: run: | cargo t tsc bindings/* --noEmit + - name: dependencies e2e test with default export env + working-directory: e2e/dependencies/consumer + run: | + TS_RS_EXPORT_DIR=custom-bindings cargo t + tsc custom-bindings/* --noEmit e2e-workspace: name: Run 'workspace' end-to-end test runs-on: ubuntu-latest @@ -27,6 +32,11 @@ jobs: run: | cargo t tsc parent/bindings/* --noEmit + - name: workspace e2e with default export env + working-directory: e2e/workspace + run: | + TS_RS_EXPORT_DIR=custom-bindings cargo t + tsc parent/custom-bindings/* --noEmit readme-up-to-date: name: Check that README.md is up-to-date From d15c352fe0c449b5b471f6b818895522f9444f7d Mon Sep 17 00:00:00 2001 From: Sammy Harris Date: Sun, 11 Feb 2024 11:31:06 -0600 Subject: [PATCH 11/12] add export_to to crate1 dependency for tests --- e2e/workspace/crate1/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/e2e/workspace/crate1/src/lib.rs b/e2e/workspace/crate1/src/lib.rs index afa054635..d1d8c22f1 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_to = "crate1/")] pub struct Crate1 { pub x: [[[i32; 128]; 128]; 128], -} \ No newline at end of file +} From 344c7405fbaaba956613f488dbc7fa2e4245465f Mon Sep 17 00:00:00 2001 From: escritorio-gustavo <131818645+escritorio-gustavo@users.noreply.github.com> Date: Mon, 12 Feb 2024 08:34:52 -0300 Subject: [PATCH 12/12] Prefer `Option<&T>` over &Option --- ts-rs/src/export.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ts-rs/src/export.rs b/ts-rs/src/export.rs index 3e3eadd61..23d96dfa5 100644 --- a/ts-rs/src/export.rs +++ b/ts-rs/src/export.rs @@ -134,9 +134,9 @@ pub mod __private { use super::*; const EXPORT_DIR_ENV_VAR: &str = "TS_RS_EXPORT_DIR"; - fn provided_default_dir() -> &'static Option { + fn provided_default_dir() -> Option<&'static str> { static EXPORT_TO: OnceLock> = OnceLock::new(); - EXPORT_TO.get_or_init(|| std::env::var(EXPORT_DIR_ENV_VAR).ok()) + EXPORT_TO.get_or_init(|| std::env::var(EXPORT_DIR_ENV_VAR).ok()).as_deref() } /// Returns the path to where `T` should be exported using the `TS_RS_EXPORT_DIR` environment variable. @@ -144,7 +144,7 @@ pub mod __private { /// 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( + provided_default_dir().map_or_else( || T::EXPORT_TO.map(ToString::to_string), |path| Some(format!("{path}/{}.ts", T::name())), )