diff --git a/Cargo.lock b/Cargo.lock index 44ac8f9..0e72dd1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "camino" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4648c6d00a709aa069a236adcaae4f605a6241c72bf5bee79331a4b625921a9" + [[package]] name = "cc" version = "1.0.68" @@ -192,6 +198,7 @@ version = "0.9.5" dependencies = [ "ansi_term 0.12.1", "atty", + "camino", "clap", "ctrlc", "derivative", diff --git a/Cargo.toml b/Cargo.toml index 46ac427..dfc662b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [".", "bin/prerelease"] [dependencies] ansi_term = "0.12.0" atty = "0.2.0" +camino = "1.0.4" clap = "2.33.0" derivative = "2.0.0" dotenv = "0.15.0" diff --git a/src/common.rs b/src/common.rs index 95c13c7..fc6ea43 100644 --- a/src/common.rs +++ b/src/common.rs @@ -19,6 +19,7 @@ pub(crate) use std::{ }; // dependencies +pub(crate) use camino::Utf8Path; pub(crate) use derivative::Derivative; pub(crate) use edit_distance::edit_distance; pub(crate) use libc::EXIT_FAILURE; diff --git a/src/function.rs b/src/function.rs index 3faed59..3612731 100644 --- a/src/function.rs +++ b/src/function.rs @@ -1,7 +1,6 @@ use crate::common::*; use Function::*; - pub(crate) enum Function { Nullary(fn(&FunctionContext) -> Result), Unary(fn(&FunctionContext, &str) -> Result), @@ -19,6 +18,11 @@ lazy_static! { ("env_var", Unary(env_var)), ("env_var_or_default", Binary(env_var_or_default)), ("just_executable", Nullary(just_executable)), + ("file_name", Unary(file_name)), + ("parent_directory", Unary(parent_directory)), + ("file_stem", Unary(file_stem)), + ("without_extension", Unary(without_extension)), + ("extension", Unary(extension)) ] .into_iter() .collect(); @@ -136,3 +140,43 @@ fn just_executable(_context: &FunctionContext) -> Result { ) }) } + +fn file_name(_context: &FunctionContext, path: &str) -> Result { + Utf8Path::new(path) + .file_name() + .map(str::to_owned) + .ok_or_else(|| format!("Could not extract file name from `{}`", path)) +} + +fn parent_directory(_context: &FunctionContext, path: &str) -> Result { + Utf8Path::new(path) + .parent() + .map(Utf8Path::to_string) + .ok_or_else(|| format!("Could not extract parent directory from `{}`", path)) +} + +fn file_stem(_context: &FunctionContext, path: &str) -> Result { + Utf8Path::new(path) + .file_stem() + .map(str::to_owned) + .ok_or_else(|| format!("Could not extract file stem from `{}`", path)) +} + +fn without_extension(_context: &FunctionContext, path: &str) -> Result { + let parent = Utf8Path::new(path) + .parent() + .ok_or_else(|| format!("Could not extract parent from `{}`", path))?; + + let file_stem = Utf8Path::new(path) + .file_stem() + .ok_or_else(|| format!("Could not extract file stem from `{}`", path))?; + + Ok(parent.join(file_stem).to_string()) +} + +fn extension(_context: &FunctionContext, path: &str) -> Result { + Utf8Path::new(path) + .extension() + .map(str::to_owned) + .ok_or_else(|| format!("Could not extract extension from `{}`", path)) +} diff --git a/tests/fmt.rs b/tests/fmt.rs index cd70f08..a051117 100644 --- a/tests/fmt.rs +++ b/tests/fmt.rs @@ -251,6 +251,25 @@ test! { ", } +test! { + name: assignment_path_functions, + justfile: " + foo := without_extension('foo/bar.baz') + foo2 := file_stem('foo/bar.baz') + foo3 := parent_directory('foo/bar.baz') + foo4 := file_name('foo/bar.baz') + foo5 := extension('foo/bar.baz') + ", + args: ("--dump"), + stdout: " + foo := without_extension('foo/bar.baz') + foo2 := file_stem('foo/bar.baz') + foo3 := parent_directory('foo/bar.baz') + foo4 := file_name('foo/bar.baz') + foo5 := extension('foo/bar.baz') + ", +} + test! { name: recipe_ordinary, justfile: " diff --git a/tests/misc.rs b/tests/misc.rs index e3cea42..d638905 100644 --- a/tests/misc.rs +++ b/tests/misc.rs @@ -1170,6 +1170,169 @@ foo: stderr: format!("/bin/echo '{}' 'HTAP' 'ABC'\n", env::var("USER").unwrap()).as_str(), } +#[cfg(not(windows))] +test! { + name: path_functions, + justfile: r#" +we := without_extension('/foo/bar/baz.hello') +fs := file_stem('/foo/bar/baz.hello') +fn := file_name('/foo/bar/baz.hello') +dir := parent_directory('/foo/bar/baz.hello') +ext := extension('/foo/bar/baz.hello') + +foo: + /bin/echo '{{we}}' '{{fs}}' '{{fn}}' '{{dir}}' '{{ext}}' +"#, + stdout: "/foo/bar/baz baz baz.hello /foo/bar hello\n", + stderr: "/bin/echo '/foo/bar/baz' 'baz' 'baz.hello' '/foo/bar' 'hello'\n", +} + +#[cfg(not(windows))] +test! { + name: path_functions2, + justfile: r#" +we := without_extension('/foo/bar/baz') +fs := file_stem('/foo/bar/baz.hello.ciao') +fn := file_name('/bar/baz.hello.ciao') +dir := parent_directory('/foo/') +ext := extension('/foo/bar/baz.hello.ciao') + +foo: + /bin/echo '{{we}}' '{{fs}}' '{{fn}}' '{{dir}}' '{{ext}}' +"#, + stdout: "/foo/bar/baz baz.hello baz.hello.ciao / ciao\n", + stderr: "/bin/echo '/foo/bar/baz' 'baz.hello' 'baz.hello.ciao' '/' 'ciao'\n", +} + +#[cfg(not(windows))] +test! { + name: broken_without_extension_function, + justfile: r#" +we := without_extension('') + +foo: + /bin/echo '{{we}}' +"#, + stdout: "", + stderr: format!("{} {}\n{}\n{}\n{}\n", + "error: Call to function `without_extension` failed:", + "Could not extract parent from ``", + " |", + "1 | we := without_extension(\'\')", + " | ^^^^^^^^^^^^^^^^^").as_str(), + status: EXIT_FAILURE, +} + +#[cfg(not(windows))] +test! { + name: broken_extension_function, + justfile: r#" +we := extension('') + +foo: + /bin/echo '{{we}}' +"#, + stdout: "", + stderr: format!("{}\n{}\n{}\n{}\n", + "error: Call to function `extension` failed: Could not extract extension from ``", + " |", + "1 | we := extension(\'\')", + " | ^^^^^^^^^").as_str(), + status: EXIT_FAILURE, +} + +#[cfg(not(windows))] +test! { + name: broken_extension_function2, + justfile: r#" +we := extension('foo') + +foo: + /bin/echo '{{we}}' +"#, + stdout: "", + stderr: format!("{}\n{}\n{}\n{}\n", + "error: Call to function `extension` failed: Could not extract extension from `foo`", + " |", + "1 | we := extension(\'foo\')", + " | ^^^^^^^^^").as_str(), + status: EXIT_FAILURE, +} + +#[cfg(not(windows))] +test! { + name: broken_file_stem_function, + justfile: r#" +we := file_stem('') + +foo: + /bin/echo '{{we}}' +"#, + stdout: "", + stderr: format!("{}\n{}\n{}\n{}\n", + "error: Call to function `file_stem` failed: Could not extract file stem from ``", + " |", + "1 | we := file_stem(\'\')", + " | ^^^^^^^^^").as_str(), + status: EXIT_FAILURE, +} + +#[cfg(not(windows))] +test! { + name: broken_file_name_function, + justfile: r#" +we := file_name('') + +foo: + /bin/echo '{{we}}' +"#, + stdout: "", + stderr: format!("{}\n{}\n{}\n{}\n", + "error: Call to function `file_name` failed: Could not extract file name from ``", + " |", + "1 | we := file_name(\'\')", + " | ^^^^^^^^^").as_str(), + status: EXIT_FAILURE, +} + +#[cfg(not(windows))] +test! { + name: broken_directory_function, + justfile: r#" +we := parent_directory('') + +foo: + /bin/echo '{{we}}' +"#, + stdout: "", + stderr: format!("{} {}\n{}\n{}\n{}\n", + "error: Call to function `parent_directory` failed:", + "Could not extract parent directory from ``", + " |", + "1 | we := parent_directory(\'\')", + " | ^^^^^^^^^^^^^^^^").as_str(), + status: EXIT_FAILURE, +} + +#[cfg(not(windows))] +test! { + name: broken_directory_function2, + justfile: r#" +we := parent_directory('/') + +foo: + /bin/echo '{{we}}' +"#, + stdout: "", + stderr: format!("{} {}\n{}\n{}\n{}\n", + "error: Call to function `parent_directory` failed:", + "Could not extract parent directory from `/`", + " |", + "1 | we := parent_directory(\'/\')", + " | ^^^^^^^^^^^^^^^^").as_str(), + status: EXIT_FAILURE, +} + #[cfg(windows)] test! { name: env_var_functions,