Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
### Features
- The `#[ts(rename)]` attribute on structs, enums and variants now accepts any expression.
This makes it possible to, for example, rename a struct to the name of a module it is contained in using `#[ts(rename = module_path!().rsplit_once("::").unwrap().1)]`
- The `#[ts(export_to)]` attribute on structs and enums now accepts any expression.
- Added `#[ts(optional_fields)]` and `#[ts(optional_fields = nullable)]` attribute to structs, this attribute is equivalent to using the corresponding `#[ts(optional)]` or `#[ts(optional = nullable)]` on every field of the struct. ([#366](https://github.com/Aleph-Alpha/ts-rs/pull/366))

### Fixes
Expand Down
4 changes: 2 additions & 2 deletions macros/src/attr/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub struct EnumAttr {
pub rename_all: Option<Inflection>,
pub rename_all_fields: Option<Inflection>,
pub rename: Option<Expr>,
pub export_to: Option<String>,
pub export_to: Option<Expr>,
pub export: bool,
pub docs: String,
pub concrete: HashMap<Ident, Type>,
Expand Down Expand Up @@ -212,7 +212,7 @@ impl_parse! {
"rename" => out.rename = Some(parse_assign_expr(input)?),
"rename_all" => out.rename_all = Some(parse_assign_inflection(input)?),
"rename_all_fields" => out.rename_all_fields = Some(parse_assign_inflection(input)?),
"export_to" => out.export_to = Some(parse_assign_str(input)?),
"export_to" => out.export_to = Some(parse_assign_expr(input)?),
"export" => out.export = true,
"tag" => out.tag = Some(parse_assign_str(input)?),
"content" => out.content = Some(parse_assign_str(input)?),
Expand Down
4 changes: 2 additions & 2 deletions macros/src/attr/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub struct StructAttr {
pub type_override: Option<String>,
pub rename_all: Option<Inflection>,
pub rename: Option<Expr>,
pub export_to: Option<String>,
pub export_to: Option<Expr>,
pub export: bool,
pub tag: Option<String>,
pub docs: String,
Expand Down Expand Up @@ -163,7 +163,7 @@ impl_parse! {
"rename_all" => out.rename_all = Some(parse_assign_inflection(input)?),
"tag" => out.tag = Some(parse_assign_str(input)?),
"export" => out.export = true,
"export_to" => out.export_to = Some(parse_assign_str(input)?),
"export_to" => out.export_to = Some(parse_assign_expr(input)?),
"concrete" => out.concrete = parse_concrete(input)?,
"bound" => out.bound = Some(parse_bound(input)?),
"optional_fields" => out.optional_fields = parse_optional(input)?,
Expand Down
22 changes: 13 additions & 9 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct DerivedTS {
bound: Option<Vec<WherePredicate>>,

export: bool,
export_to: Option<String>,
export_to: Option<Expr>,
}

impl DerivedTS {
Expand All @@ -42,14 +42,18 @@ impl DerivedTS {
let output_path_fn = {
let ts_name = &self.ts_name;
// expression of type `String` containing the file path
let path_string = match self.export_to.as_deref() {
Some(dirname) if dirname.ends_with('/') => {
quote![format!("{}{}.ts", #dirname, #ts_name)]
}
Some(filename) => quote![#filename.to_owned()],
None => {
quote![format!("{}.ts", #ts_name)]
}
let path_string = match &self.export_to {
Some(dir_or_file) => quote![{
let dir_or_file = format!("{}", #dir_or_file);
if dir_or_file.ends_with('/') {
// export into directory
format!("{dir_or_file}{}.ts", #ts_name)
} else {
// export into provided file
format!("{dir_or_file}")
}
}],
None => quote![format!("{}.ts", #ts_name)],
};

quote! {
Expand Down
3 changes: 2 additions & 1 deletion ts-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ mod tokio;
/// Specifies where the type should be exported to. Defaults to `<name>.ts`.
/// The path given to the `export_to` attribute is relative to the `TS_RS_EXPORT_DIR` environment variable,
/// or, if `TS_RS_EXPORT_DIR` is not set, to `./bindings`
/// If the provided path ends in a trailing `/`, it is interpreted as a directory.
/// If the provided path ends in a trailing `/`, it is interpreted as a directory.
/// This attribute also accepts arbitrary expressions.
/// Note that you need to add the `export` attribute as well, in order to generate a test which exports the type.
/// <br/><br/>
///
Expand Down
100 changes: 100 additions & 0 deletions ts-rs/tests/integration/export_to.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use std::path::Path;
use ts_rs::TS;

#[derive(TS)]
#[ts(export, export_to = "export_to/with_str_to_file.ts")]
struct WithStrToFile;

#[derive(TS)]
#[ts(export, export_to = "export_to/")]
struct WithStrToDir;

// --

#[derive(TS)]
#[ts(export, export_to = &"export_to/with_str_ref_to_file.ts")]
struct WithStrRefToFile;

#[derive(TS)]
#[ts(export, export_to = &"export_to/")]
struct WithStrRefToDir;

// --

#[derive(TS)]
#[ts(export, export_to = format!("export_to/with_string_to_file.ts"))]
struct WithStringToFile;

#[derive(TS)]
#[ts(export, export_to = format!("export_to/"))]
struct WithStringToDir;

// --

#[derive(TS)]
#[ts(export, export_to = &format!("export_to/with_string_ref_to_file.ts"))]
struct WithStringRefToFile;

#[derive(TS)]
#[ts(export, export_to = &format!("export_to/"))]
struct WithStringRefToDir;

// --

#[derive(TS)]
#[ts(export, export_to = {
let dir = WithStrToFile::default_output_path().unwrap();
let dir = dir.parent().unwrap();
let file = dir.join("to_absolute_file_path.ts");
let file = std::path::absolute(file).unwrap();
file.display().to_string()
})]
struct ToAbsoluteFilePath(WithStrToDir, WithStrToFile);

#[derive(TS)]
#[ts(export, export_to = {
let dir = WithStrToFile::default_output_path().unwrap();
let dir = dir.parent().unwrap();
let dir = std::path::absolute(dir).unwrap();
let dir = dir.display();
format!("{dir}/")
})]
struct ToAbsoluteDirPath(WithStrToDir, WithStrToFile, ToAbsoluteFilePath);

// --

#[test]
#[cfg(test)]
fn check_export_complete() {
export_bindings_withstrtofile();
export_bindings_withstrtodir();
export_bindings_withstrreftofile();
export_bindings_withstrreftodir();
export_bindings_withstringtofile();
export_bindings_withstringtodir();
export_bindings_withstringreftofile();
export_bindings_withstringreftodir();
export_bindings_toabsolutefilepath();
export_bindings_toabsolutedirpath();

let files = [
"with_str_to_file.ts",
"WithStrToDir.ts",
"with_str_ref_to_file.ts",
"WithStrRefToDir.ts",
"with_string_to_file.ts",
"WithStringToDir.ts",
"with_string_ref_to_file.ts",
"WithStringRefToDir.ts",
"to_absolute_file_path.ts",
"ToAbsoluteDirPath.ts",
];

let dir = std::env::var("TS_RS_EXPORT_DIR").unwrap_or_else(|_| "./bindings".to_owned());
let dir = Path::new(&dir).join("export_to");

files
.iter()
.map(|file| dir.join(file))
.for_each(|file| assert!(file.is_file(), "{file:?}"));
}
1 change: 1 addition & 0 deletions ts-rs/tests/integration/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod enum_flattening_nested;
mod enum_struct_rename_all;
mod enum_variant_annotation;
mod export_manually;
mod export_to;
mod field_rename;
mod flatten;
mod generic_fields;
Expand Down