Expand tilde in import and module paths (#1792)

This commit is contained in:
Casey Rodarmor 2023-12-28 18:08:02 -08:00 committed by GitHub
parent b21819dcf2
commit f039701f71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 142 additions and 4 deletions

70
Cargo.lock generated
View File

@ -156,6 +156,27 @@ dependencies = [
"crypto-common", "crypto-common",
] ]
[[package]]
name = "dirs"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "doc-comment" name = "doc-comment"
version = "0.3.3" version = "0.3.3"
@ -326,6 +347,7 @@ dependencies = [
"cradle", "cradle",
"ctrlc", "ctrlc",
"derivative", "derivative",
"dirs",
"dotenvy", "dotenvy",
"edit-distance", "edit-distance",
"env_logger", "env_logger",
@ -372,6 +394,17 @@ version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "libredox"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
dependencies = [
"bitflags 2.4.1",
"libc",
"redox_syscall",
]
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.6" version = "0.5.6"
@ -423,6 +456,12 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]] [[package]]
name = "pretty_assertions" name = "pretty_assertions"
version = "1.4.0" version = "1.4.0"
@ -505,6 +544,17 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
] ]
[[package]]
name = "redox_users"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
dependencies = [
"getrandom",
"libredox",
"thiserror",
]
[[package]] [[package]]
name = "ref-type" name = "ref-type"
version = "0.0.0" version = "0.0.0"
@ -785,6 +835,26 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "thiserror"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.43",
]
[[package]] [[package]]
name = "typed-arena" name = "typed-arena"
version = "2.0.2" version = "2.0.2"

View File

@ -24,6 +24,7 @@ camino = "1.0.4"
clap = { version = "2.33.0", features = ["wrap_help"] } clap = { version = "2.33.0", features = ["wrap_help"] }
ctrlc = { version = "3.1.1", features = ["termination"] } ctrlc = { version = "3.1.1", features = ["termination"] }
derivative = "2.0.0" derivative = "2.0.0"
dirs = "5.0.1"
dotenvy = "0.15" dotenvy = "0.15"
edit-distance = "2.0.0" edit-distance = "2.0.0"
env_logger = "0.10.0" env_logger = "0.10.0"

View File

@ -2358,7 +2358,8 @@ A
``` ```
The `import` path can be absolute or relative to the location of the justfile The `import` path can be absolute or relative to the location of the justfile
containing it. containing it. A leading `~/` in the import path is replaced with the current
users home directory.
Justfiles are insensitive to order, so included files can reference variables Justfiles are insensitive to order, so included files can reference variables
and recipes defined after the `import` statement. and recipes defined after the `import` statement.
@ -2406,7 +2407,19 @@ If a module is named `foo`, just will search for the module file in `foo.just`,
`foo/mod.just`, `foo/justfile`, and `foo/.justfile`. In the latter two cases, `foo/mod.just`, `foo/justfile`, and `foo/.justfile`. In the latter two cases,
the module file may have any capitalization. the module file may have any capitalization.
Environment files are loaded for the root justfile. Module statements may be of the form:
```mf
mod foo 'PATH'
```
Which loads the module's source file from `PATH`, instead of from the usual
locations. A leading `~/` in `PATH` is replaced with the current user's home
directory.
Environment files are only loaded for the root justfile, and loaded environment
variables are available in submodules. Settings in submodules that affect
enviroment file loading are ignored.
Recipes in submodules without the `[no-cd]` attribute run with the working Recipes in submodules without the `[no-cd]` attribute run with the working
directory set to the directory containing the submodule source file. directory set to the directory containing the submodule source file.

View File

@ -41,7 +41,7 @@ impl Compiler {
let parent = current.parent().unwrap(); let parent = current.parent().unwrap();
let import = if let Some(path) = path { let import = if let Some(path) = path {
parent.join(&path.cooked) parent.join(Self::expand_tilde(&path.cooked)?)
} else { } else {
Self::find_module_file(parent, *name)? Self::find_module_file(parent, *name)?
}; };
@ -53,7 +53,11 @@ impl Compiler {
stack.push((import, depth + 1)); stack.push((import, depth + 1));
} }
Item::Import { relative, absolute } => { Item::Import { relative, absolute } => {
let import = current.parent().unwrap().join(&relative.cooked).lexiclean(); let import = current
.parent()
.unwrap()
.join(Self::expand_tilde(&relative.cooked)?)
.lexiclean();
if srcs.contains_key(&import) { if srcs.contains_key(&import) {
return Err(Error::CircularImport { current, import }); return Err(Error::CircularImport { current, import });
} }
@ -117,6 +121,16 @@ impl Compiler {
} }
} }
fn expand_tilde(path: &str) -> RunResult<'static, PathBuf> {
Ok(if let Some(path) = path.strip_prefix("~/") {
dirs::home_dir()
.ok_or(Error::Homedir)?
.join(path.trim_start_matches('/'))
} else {
PathBuf::from(path)
})
}
#[cfg(test)] #[cfg(test)]
pub(crate) fn test_compile(src: &str) -> CompileResult<Justfile> { pub(crate) fn test_compile(src: &str) -> CompileResult<Justfile> {
let tokens = Lexer::test_lex(src)?; let tokens = Lexer::test_lex(src)?;

View File

@ -95,6 +95,7 @@ pub(crate) enum Error<'src> {
GetConfirmation { GetConfirmation {
io_error: io::Error, io_error: io::Error,
}, },
Homedir,
InitExists { InitExists {
justfile: PathBuf, justfile: PathBuf,
}, },
@ -347,6 +348,9 @@ impl<'src> ColorDisplay for Error<'src> {
GetConfirmation { io_error } => { GetConfirmation { io_error } => {
write!(f, "Failed to read confirmation from stdin: {io_error}")?; write!(f, "Failed to read confirmation from stdin: {io_error}")?;
} }
Homedir => {
write!(f, "Failed to get homedir")?;
}
InitExists { justfile } => { InitExists { justfile } => {
write!(f, "Justfile `{}` already exists", justfile.display())?; write!(f, "Justfile `{}` already exists", justfile.display())?;
} }

View File

@ -152,3 +152,20 @@ fn recipes_in_import_are_overridden_by_recipes_in_parent() {
.stdout("ROOT\n") .stdout("ROOT\n")
.run(); .run();
} }
#[cfg(not(windows))]
#[test]
fn import_paths_beginning_with_tilde_are_expanded_to_homdir() {
Test::new()
.write("foobar/mod.just", "foo:\n @echo FOOBAR")
.justfile(
"
import '~/mod.just'
",
)
.test_round_trip(false)
.arg("foo")
.stdout("FOOBAR\n")
.env("HOME", "foobar")
.run();
}

View File

@ -529,3 +529,22 @@ fn submodule_shebang_recipes_run_in_submodule_directory() {
.stdout("BAR") .stdout("BAR")
.run(); .run();
} }
#[cfg(not(windows))]
#[test]
fn module_paths_beginning_with_tilde_are_expanded_to_homdir() {
Test::new()
.write("foobar/mod.just", "foo:\n @echo FOOBAR")
.justfile(
"
mod foo '~/mod.just'
",
)
.test_round_trip(false)
.arg("--unstable")
.arg("foo")
.arg("foo")
.stdout("FOOBAR\n")
.env("HOME", "foobar")
.run();
}