Skip to content

Commit

Permalink
Merge pull request #247 from Aleph-Alpha/repro-path-bug
Browse files Browse the repository at this point in the history
bug: `#[ts(export_to = "../path")]` can cause `diff_paths` to fail
  • Loading branch information
escritorio-gustavo authored Mar 6, 2024
2 parents 676cc6e + 00b390a commit ffac0e2
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 51 deletions.
85 changes: 43 additions & 42 deletions ts-rs/src/export.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod path;

use std::{
any::TypeId,
collections::BTreeMap,
Expand All @@ -7,7 +9,6 @@ use std::{
};

use thiserror::Error;
use ExportError::*;

use crate::TS;

Expand Down Expand Up @@ -107,8 +108,8 @@ pub(crate) fn export_type_to<T: TS + ?Sized + 'static, P: AsRef<Path>>(
use dprint_plugin_typescript::{configuration::ConfigurationBuilder, format_text};

let fmt_cfg = ConfigurationBuilder::new().deno().build();
if let Some(formatted) =
format_text(path.as_ref(), &buffer, &fmt_cfg).map_err(|e| Formatting(e.to_string()))?
if let Some(formatted) = format_text(path.as_ref(), &buffer, &fmt_cfg)
.map_err(|e| ExportError::Formatting(e.to_string()))?
{
buffer = formatted;
}
Expand Down Expand Up @@ -158,11 +159,11 @@ pub(crate) fn export_type_to_string<T: TS + ?Sized + 'static>() -> Result<String

/// Compute the output path to where `T` should be exported.
fn output_path<T: TS + ?Sized>() -> Result<PathBuf, ExportError> {
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::<T>()))?);
Ok(manifest_dir.join(path))
path::absolute(Path::new(
&T::get_export_to()
.ok_or_else(|| std::any::type_name::<T>())
.map_err(ExportError::CannotBeExported)?,
))
}

/// Push the declaration of `T`
Expand All @@ -180,7 +181,8 @@ fn generate_decl<T: TS + ?Sized>(out: &mut String) {

/// Push an import statement for all dependencies of `T`
fn generate_imports<T: TS + ?Sized + 'static>(out: &mut String) -> Result<(), ExportError> {
let export_to = T::get_export_to().ok_or(CannotBeExported(std::any::type_name::<T>()))?;
let export_to =
T::get_export_to().ok_or(ExportError::CannotBeExported(std::any::type_name::<T>()))?;
let path = Path::new(&export_to);

let deps = T::dependencies();
Expand Down Expand Up @@ -233,47 +235,46 @@ fn import_path(from: &Path, import: &Path) -> String {
//
// Adapted from rustc's path_relative_from
// https://github.com/rust-lang/rust/blob/e1d0de82cc40b666b88d4a6d2c9dcbc81d7ed27f/src/librustc_back/rpath.rs#L116-L158
fn diff_paths<P, B>(path: P, base: B) -> Option<PathBuf>
fn diff_paths<P, B>(path: P, base: B) -> Result<PathBuf, ExportError>
where
P: AsRef<Path>,
B: AsRef<Path>,
{
let path = path.as_ref();
let base = base.as_ref();
use Component as C;

if path.is_absolute() != base.is_absolute() {
if path.is_absolute() {
Some(PathBuf::from(path))
} else {
None
}
} else {
let mut ita = path.components();
let mut itb = base.components();
let mut comps: Vec<Component> = vec![];
loop {
match (ita.next(), itb.next()) {
(None, None) => break,
(Some(a), None) => {
comps.push(a);
comps.extend(ita.by_ref());
break;
}
(None, _) => comps.push(Component::ParentDir),
(Some(a), Some(b)) if comps.is_empty() && a == b => (),
(Some(a), Some(Component::CurDir)) => comps.push(a),
(Some(_), Some(Component::ParentDir)) => return None,
(Some(a), Some(_)) => {
let path = path::absolute(path)?;
let base = path::absolute(base)?;

let mut ita = path.components();
let mut itb = base.components();
let mut comps: Vec<Component> = vec![];

loop {
match (ita.next(), itb.next()) {
(Some(C::ParentDir | C::CurDir), _) | (_, Some(C::ParentDir | C::CurDir)) => {
unreachable!(
"The paths have been cleaned, no no '.' or '..' components are present"
)
}
(None, None) => break,
(Some(a), None) => {
comps.push(a);
comps.extend(ita.by_ref());
break;
}
(None, _) => comps.push(Component::ParentDir),
(Some(a), Some(b)) if comps.is_empty() && a == b => (),
(Some(a), Some(_)) => {
comps.push(Component::ParentDir);
for _ in itb {
comps.push(Component::ParentDir);
for _ in itb {
comps.push(Component::ParentDir);
}
comps.push(a);
comps.extend(ita.by_ref());
break;
}
comps.push(a);
comps.extend(ita.by_ref());
break;
}
}
Some(comps.iter().map(|c| c.as_os_str()).collect())
}

Ok(comps.iter().map(|c| c.as_os_str()).collect())
}
31 changes: 31 additions & 0 deletions ts-rs/src/export/path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use std::path::{Component as C, Path, PathBuf};

use super::ExportError as E;

const ERROR_MESSAGE: &str = r#"The path provided with `#[ts(export_to = "..")]` is not valid"#;
pub fn absolute<T: AsRef<Path>>(path: T) -> Result<PathBuf, E> {
let path = path.as_ref();

if path.is_absolute() {
return Ok(path.to_owned());
}

let path = std::env::current_dir()?.join(path);

let mut out = Vec::new();
for comp in path.components() {
match comp {
C::CurDir => (),
C::ParentDir => {
out.pop().ok_or(E::CannotBeExported(ERROR_MESSAGE))?;
}
comp => out.push(comp),
}
}

Ok(if !out.is_empty() {
out.iter().collect()
} else {
PathBuf::from(".")
})
}
10 changes: 2 additions & 8 deletions ts-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,10 +562,7 @@ impl<T: TS, E: TS> TS for Result<T, E> {
where
Self: 'static,
{
T::generics()
.push::<T>()
.extend(E::generics())
.push::<E>()
T::generics().push::<T>().extend(E::generics()).push::<E>()
}
}

Expand Down Expand Up @@ -658,10 +655,7 @@ impl<K: TS, V: TS, H> TS for HashMap<K, V, H> {
where
Self: 'static,
{
K::generics()
.push::<K>()
.extend(V::generics())
.push::<V>()
K::generics().push::<K>().extend(V::generics()).push::<V>()
}
}

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

#[derive(TS)]
#[ts(export, export_to = "../ts-rs/tests-out/path_bug/")]
struct Foo {
bar: Bar,
}

#[derive(TS)]
#[ts(export_to = "tests-out/path_bug/aaa/")]
struct Bar {
i: i32,
}

#[test]
fn path_bug() {
Foo::export().unwrap();
Bar::export().unwrap();

let base = std::env::current_dir().unwrap();
assert!(base.join("./tests-out/path_bug/Foo.ts").is_file());
assert!(base.join("./tests-out/path_bug/aaa/Bar.ts").is_file());
}
1 change: 0 additions & 1 deletion ts-rs/tests/struct_tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,3 @@ fn test() {
"{ type: \"TaggedType\", a: number, b: number, }"
)
}

0 comments on commit ffac0e2

Please sign in to comment.