mirror of
https://github.com/nushell/nushell
synced 2024-12-26 21:13:19 +00:00
allow multiple extensions (#10593)
<!-- if this PR closes one or more issues, you can automatically link the PR with them by using one of the [*linking keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword), e.g. - this PR should close #xxxx - fixes #xxxx you can also mention related issues, PRs or discussions! --> # Description <!-- Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes. Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience. --> This PR allows `open` to handle files with multiple extensions; i.e it will try to call `from tar.gz`, `from gz` when calling ```nu open file.tar.gz ``` # User-Facing Changes <!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. --> No breaking changes.
This commit is contained in:
parent
6cff54ed0d
commit
1751ac12f4
2 changed files with 109 additions and 30 deletions
|
@ -171,36 +171,35 @@ impl Command for Open {
|
|||
metadata: None,
|
||||
trim_end_newline: false,
|
||||
};
|
||||
let ext = if raw {
|
||||
let exts_opt: Option<Vec<String>> = if raw {
|
||||
None
|
||||
} else {
|
||||
path.extension()
|
||||
.map(|name| name.to_string_lossy().to_string().to_lowercase())
|
||||
let path_str = path
|
||||
.file_name()
|
||||
.unwrap_or(std::ffi::OsStr::new(path))
|
||||
.to_string_lossy()
|
||||
.to_lowercase();
|
||||
Some(extract_extensions(path_str.as_str()))
|
||||
};
|
||||
|
||||
if let Some(ext) = ext {
|
||||
match engine_state.find_decl(format!("from {ext}").as_bytes(), &[]) {
|
||||
Some(converter_id) => {
|
||||
let decl = engine_state.get_decl(converter_id);
|
||||
let command_output = if let Some(block_id) = decl.get_block_id() {
|
||||
let block = engine_state.get_block(block_id);
|
||||
eval_block(
|
||||
engine_state,
|
||||
stack,
|
||||
block,
|
||||
file_contents,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
decl.run(
|
||||
engine_state,
|
||||
stack,
|
||||
&Call::new(call_span),
|
||||
file_contents,
|
||||
)
|
||||
};
|
||||
output.push(command_output.map_err(|inner| {
|
||||
let converter = exts_opt.and_then(|exts| {
|
||||
exts.iter().find_map(|ext| {
|
||||
engine_state
|
||||
.find_decl(format!("from {}", ext).as_bytes(), &[])
|
||||
.map(|id| (id, ext.to_string()))
|
||||
})
|
||||
});
|
||||
|
||||
match converter {
|
||||
Some((converter_id, ext)) => {
|
||||
let decl = engine_state.get_decl(converter_id);
|
||||
let command_output = if let Some(block_id) = decl.get_block_id() {
|
||||
let block = engine_state.get_block(block_id);
|
||||
eval_block(engine_state, stack, block, file_contents, false, false)
|
||||
} else {
|
||||
decl.run(engine_state, stack, &Call::new(call_span), file_contents)
|
||||
};
|
||||
output.push(command_output.map_err(|inner| {
|
||||
ShellError::GenericError(
|
||||
format!("Error while parsing as {ext}"),
|
||||
format!("Could not parse '{}' with `from {}`", path.display(), ext),
|
||||
|
@ -209,11 +208,8 @@ impl Command for Open {
|
|||
vec![inner],
|
||||
)
|
||||
})?);
|
||||
}
|
||||
None => output.push(file_contents),
|
||||
}
|
||||
} else {
|
||||
output.push(file_contents)
|
||||
None => output.push(file_contents),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -265,3 +261,23 @@ fn permission_denied(dir: impl AsRef<Path>) -> bool {
|
|||
Ok(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_extensions(filename: &str) -> Vec<String> {
|
||||
let parts: Vec<&str> = filename.split('.').collect();
|
||||
let mut extensions: Vec<String> = Vec::new();
|
||||
let mut current_extension = String::new();
|
||||
|
||||
for part in parts.iter().rev() {
|
||||
if current_extension.is_empty() {
|
||||
current_extension.push_str(part);
|
||||
} else {
|
||||
current_extension = format!("{}.{}", part, current_extension);
|
||||
}
|
||||
extensions.push(current_extension.clone());
|
||||
}
|
||||
|
||||
extensions.pop();
|
||||
extensions.reverse();
|
||||
|
||||
extensions
|
||||
}
|
||||
|
|
|
@ -34,6 +34,69 @@ fn parses_file_with_uppercase_extension() {
|
|||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_file_with_multiple_extensions() {
|
||||
Playground::setup("open_test_multiple_extensions", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![
|
||||
FileWithContent("file.tar.gz", "this is a tar.gz file"),
|
||||
FileWithContent("file.tar.xz", "this is a tar.xz file"),
|
||||
]);
|
||||
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), pipeline(
|
||||
r#"
|
||||
hide "from tar.gz" ;
|
||||
hide "from gz" ;
|
||||
|
||||
def "from tar.gz" [] { 'opened tar.gz' } ;
|
||||
def "from gz" [] { 'opened gz' } ;
|
||||
open file.tar.gz
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual.out, "opened tar.gz");
|
||||
|
||||
let actual2 = nu!(
|
||||
cwd: dirs.test(), pipeline(
|
||||
r#"
|
||||
hide "from tar.xz" ;
|
||||
hide "from xz" ;
|
||||
hide "from tar" ;
|
||||
|
||||
def "from tar" [] { 'opened tar' } ;
|
||||
def "from xz" [] { 'opened xz' } ;
|
||||
open file.tar.xz
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual2.out, "opened xz");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_dotfile() {
|
||||
Playground::setup("open_test_dotfile", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
".gitignore",
|
||||
r#"
|
||||
/target/
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), pipeline(
|
||||
r#"
|
||||
hide "from gitignore" ;
|
||||
|
||||
def "from gitignore" [] { 'opened gitignore' } ;
|
||||
open .gitignore
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual.out, "opened gitignore");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_csv() {
|
||||
Playground::setup("open_test_1", |dirs, sandbox| {
|
||||
|
|
Loading…
Reference in a new issue