mirror of
https://github.com/nushell/nushell.git
synced 2025-02-17 02:50:56 +01:00
Merge branch 'main' of https://github.com/nushell/engine-q into source-command
This commit is contained in:
commit
d481d5ca96
40
Cargo.lock
generated
40
Cargo.lock
generated
@ -98,6 +98,15 @@ dependencies = [
|
|||||||
"regex-automata",
|
"regex-automata",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byte-unit"
|
||||||
|
version = "4.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "063197e6eb4b775b64160dedde7a0986bb2836cce140e9492e9e96f28e18bcd8"
|
||||||
|
dependencies = [
|
||||||
|
"utf8-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.70"
|
version = "1.0.70"
|
||||||
@ -119,10 +128,20 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"serde",
|
||||||
"time",
|
"time",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono-humanize"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2eddc119501d583fd930cb92144e605f44e0252c38dd89d9247fffa1993375cb"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@ -487,6 +506,7 @@ dependencies = [
|
|||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
"nu-parser",
|
"nu-parser",
|
||||||
|
"nu-path",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
"reedline",
|
"reedline",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -496,6 +516,7 @@ dependencies = [
|
|||||||
name = "nu-command"
|
name = "nu-command"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"glob",
|
"glob",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
"nu-json",
|
"nu-json",
|
||||||
@ -549,6 +570,9 @@ dependencies = [
|
|||||||
name = "nu-protocol"
|
name = "nu-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"byte-unit",
|
||||||
|
"chrono",
|
||||||
|
"chrono-humanize",
|
||||||
"miette",
|
"miette",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -803,7 +827,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "reedline"
|
name = "reedline"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/jntrnr/reedline?branch=main#bfddc5870ca2d8301694b4211bdcdb29e647c6f3"
|
source = "git+https://github.com/nushell/reedline?branch=main#88bded3417e7f6c1242b444f403448de583357f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
@ -947,9 +971,9 @@ checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "supports-color"
|
name = "supports-color"
|
||||||
version = "1.2.0"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f3cef55878ee693bb9f6765515f52910ec20b776d222fce5d11fbb9f5368028"
|
checksum = "4872ced36b91d47bae8a214a683fe54e7078875b399dfa251df346c9b547d1f9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"is_ci",
|
"is_ci",
|
||||||
@ -975,9 +999,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.77"
|
version = "1.0.78"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0"
|
checksum = "a4eac2e6c19f5c3abc0c229bea31ff0b9b091c7b14990e8924b92902a303a0c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1098,6 +1122,12 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8-width"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7cf7d77f457ef8dfa11e4cd5933c5ddb5dc52a94664071951219a97710f0a32b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wait-timeout"
|
name = "wait-timeout"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -9,7 +9,7 @@ edition = "2018"
|
|||||||
members = ["crates/nu-cli", "crates/nu-engine", "crates/nu-parser", "crates/nu-command", "crates/nu-protocol"]
|
members = ["crates/nu-cli", "crates/nu-engine", "crates/nu-parser", "crates/nu-command", "crates/nu-protocol"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
reedline = { git = "https://github.com/jntrnr/reedline", branch = "main" }
|
reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
||||||
crossterm = "0.21.*"
|
crossterm = "0.21.*"
|
||||||
nu-cli = { path="./crates/nu-cli" }
|
nu-cli = { path="./crates/nu-cli" }
|
||||||
nu-command = { path="./crates/nu-command" }
|
nu-command = { path="./crates/nu-command" }
|
||||||
|
@ -5,9 +5,11 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine" }
|
nu-engine = { path = "../nu-engine" }
|
||||||
|
nu-path = { path = "../nu-path" }
|
||||||
nu-parser = { path = "../nu-parser" }
|
nu-parser = { path = "../nu-parser" }
|
||||||
nu-protocol = { path = "../nu-protocol" }
|
nu-protocol = { path = "../nu-protocol" }
|
||||||
|
|
||||||
miette = { version = "3.0.0", features = ["fancy"] }
|
miette = { version = "3.0.0", features = ["fancy"] }
|
||||||
thiserror = "1.0.29"
|
thiserror = "1.0.29"
|
||||||
nu-ansi-term = "0.36.0"
|
nu-ansi-term = "0.36.0"
|
||||||
reedline = { git = "https://github.com/jntrnr/reedline", branch = "main" }
|
reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
||||||
|
@ -8,6 +8,8 @@ use nu_protocol::{
|
|||||||
};
|
};
|
||||||
use reedline::Completer;
|
use reedline::Completer;
|
||||||
|
|
||||||
|
const SEP: char = std::path::MAIN_SEPARATOR;
|
||||||
|
|
||||||
pub struct NuCompleter {
|
pub struct NuCompleter {
|
||||||
engine_state: Rc<RefCell<EngineState>>,
|
engine_state: Rc<RefCell<EngineState>>,
|
||||||
}
|
}
|
||||||
@ -30,6 +32,40 @@ impl Completer for NuCompleter {
|
|||||||
|
|
||||||
for flat in flattened {
|
for flat in flattened {
|
||||||
if pos >= flat.0.start && pos <= flat.0.end {
|
if pos >= flat.0.start && pos <= flat.0.end {
|
||||||
|
let prefix = working_set.get_span_contents(flat.0);
|
||||||
|
if prefix.starts_with(b"$") {
|
||||||
|
let mut output = vec![];
|
||||||
|
|
||||||
|
for scope in &working_set.delta.scope {
|
||||||
|
for v in &scope.vars {
|
||||||
|
if v.0.starts_with(prefix) {
|
||||||
|
output.push((
|
||||||
|
reedline::Span {
|
||||||
|
start: flat.0.start - offset,
|
||||||
|
end: flat.0.end - offset,
|
||||||
|
},
|
||||||
|
String::from_utf8_lossy(v.0).to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for scope in &engine_state.scope {
|
||||||
|
for v in &scope.vars {
|
||||||
|
if v.0.starts_with(prefix) {
|
||||||
|
output.push((
|
||||||
|
reedline::Span {
|
||||||
|
start: flat.0.start - offset,
|
||||||
|
end: flat.0.end - offset,
|
||||||
|
},
|
||||||
|
String::from_utf8_lossy(v.0).to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
match &flat.1 {
|
match &flat.1 {
|
||||||
nu_parser::FlatShape::Custom(custom_completion) => {
|
nu_parser::FlatShape::Custom(custom_completion) => {
|
||||||
let prefix = working_set.get_span_contents(flat.0).to_vec();
|
let prefix = working_set.get_span_contents(flat.0).to_vec();
|
||||||
@ -80,6 +116,27 @@ impl Completer for NuCompleter {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
|
nu_parser::FlatShape::Filepath
|
||||||
|
| nu_parser::FlatShape::GlobPattern
|
||||||
|
| nu_parser::FlatShape::ExternalArg => {
|
||||||
|
let prefix = working_set.get_span_contents(flat.0);
|
||||||
|
let prefix = String::from_utf8_lossy(prefix).to_string();
|
||||||
|
|
||||||
|
let results = file_path_completion(flat.0, &prefix);
|
||||||
|
|
||||||
|
return results
|
||||||
|
.into_iter()
|
||||||
|
.map(move |x| {
|
||||||
|
(
|
||||||
|
reedline::Span {
|
||||||
|
start: x.0.start - offset,
|
||||||
|
end: x.0.end - offset,
|
||||||
|
},
|
||||||
|
x.1,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,3 +145,56 @@ impl Completer for NuCompleter {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn file_path_completion(
|
||||||
|
span: nu_protocol::Span,
|
||||||
|
partial: &str,
|
||||||
|
) -> Vec<(nu_protocol::Span, String)> {
|
||||||
|
use std::path::{is_separator, Path};
|
||||||
|
|
||||||
|
let (base_dir_name, partial) = {
|
||||||
|
// If partial is only a word we want to search in the current dir
|
||||||
|
let (base, rest) = partial.rsplit_once(is_separator).unwrap_or((".", partial));
|
||||||
|
// On windows, this standardizes paths to use \
|
||||||
|
let mut base = base.replace(is_separator, &SEP.to_string());
|
||||||
|
|
||||||
|
// rsplit_once removes the separator
|
||||||
|
base.push(SEP);
|
||||||
|
(base, rest)
|
||||||
|
};
|
||||||
|
|
||||||
|
let base_dir = nu_path::expand_path(&base_dir_name);
|
||||||
|
// This check is here as base_dir.read_dir() with base_dir == "" will open the current dir
|
||||||
|
// which we don't want in this case (if we did, base_dir would already be ".")
|
||||||
|
if base_dir == Path::new("") {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(result) = base_dir.read_dir() {
|
||||||
|
result
|
||||||
|
.filter_map(|entry| {
|
||||||
|
entry.ok().and_then(|entry| {
|
||||||
|
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
||||||
|
if matches(partial, &file_name) {
|
||||||
|
let mut path = format!("{}{}", base_dir_name, file_name);
|
||||||
|
if entry.path().is_dir() {
|
||||||
|
path.push(SEP);
|
||||||
|
file_name.push(SEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((span, path))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matches(partial: &str, from: &str) -> bool {
|
||||||
|
from.to_ascii_lowercase()
|
||||||
|
.starts_with(&partial.to_ascii_lowercase())
|
||||||
|
}
|
||||||
|
@ -83,6 +83,14 @@ impl Highlighter for NuHighlighter {
|
|||||||
Style::new().fg(nu_ansi_term::Color::Yellow).bold(),
|
Style::new().fg(nu_ansi_term::Color::Yellow).bold(),
|
||||||
next_token,
|
next_token,
|
||||||
)),
|
)),
|
||||||
|
FlatShape::Filepath => output.push((
|
||||||
|
Style::new().fg(nu_ansi_term::Color::Yellow).bold(),
|
||||||
|
next_token,
|
||||||
|
)),
|
||||||
|
FlatShape::GlobPattern => output.push((
|
||||||
|
Style::new().fg(nu_ansi_term::Color::Yellow).bold(),
|
||||||
|
next_token,
|
||||||
|
)),
|
||||||
FlatShape::Variable => output.push((
|
FlatShape::Variable => output.push((
|
||||||
Style::new().fg(nu_ansi_term::Color::Blue).bold(),
|
Style::new().fg(nu_ansi_term::Color::Blue).bold(),
|
||||||
next_token,
|
next_token,
|
||||||
|
@ -16,3 +16,4 @@ nu-table = { path = "../nu-table" }
|
|||||||
glob = "0.3.0"
|
glob = "0.3.0"
|
||||||
thiserror = "1.0.29"
|
thiserror = "1.0.29"
|
||||||
sysinfo = "0.20.4"
|
sysinfo = "0.20.4"
|
||||||
|
chrono = { version="0.4.19", features=["serde"] }
|
||||||
|
@ -17,7 +17,7 @@ impl Command for Source {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("source").required(
|
Signature::build("source").required(
|
||||||
"filename",
|
"filename",
|
||||||
SyntaxShape::FilePath,
|
SyntaxShape::Filepath,
|
||||||
"the filepath to the script file to source",
|
"the filepath to the script file to source",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ pub fn create_default_context() -> Rc<RefCell<EngineState>> {
|
|||||||
working_set.add_decl(Box::new(Benchmark));
|
working_set.add_decl(Box::new(Benchmark));
|
||||||
working_set.add_decl(Box::new(BuildString));
|
working_set.add_decl(Box::new(BuildString));
|
||||||
working_set.add_decl(Box::new(Cd));
|
working_set.add_decl(Box::new(Cd));
|
||||||
|
working_set.add_decl(Box::new(Cp));
|
||||||
working_set.add_decl(Box::new(Def));
|
working_set.add_decl(Box::new(Def));
|
||||||
working_set.add_decl(Box::new(Do));
|
working_set.add_decl(Box::new(Do));
|
||||||
working_set.add_decl(Box::new(Each));
|
working_set.add_decl(Box::new(Each));
|
||||||
@ -35,6 +36,7 @@ pub fn create_default_context() -> Rc<RefCell<EngineState>> {
|
|||||||
working_set.add_decl(Box::new(Lines));
|
working_set.add_decl(Box::new(Lines));
|
||||||
working_set.add_decl(Box::new(Ls));
|
working_set.add_decl(Box::new(Ls));
|
||||||
working_set.add_decl(Box::new(Module));
|
working_set.add_decl(Box::new(Module));
|
||||||
|
working_set.add_decl(Box::new(Mv));
|
||||||
working_set.add_decl(Box::new(Ps));
|
working_set.add_decl(Box::new(Ps));
|
||||||
working_set.add_decl(Box::new(Select));
|
working_set.add_decl(Box::new(Select));
|
||||||
working_set.add_decl(Box::new(Sys));
|
working_set.add_decl(Box::new(Sys));
|
||||||
|
@ -15,7 +15,7 @@ impl Command for Cd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("cd").optional("path", SyntaxShape::FilePath, "the path to change to")
|
Signature::build("cd").optional("path", SyntaxShape::Filepath, "the path to change to")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -28,7 +28,7 @@ impl Command for Cd {
|
|||||||
|
|
||||||
let path = match path {
|
let path = match path {
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
let path = nu_path::expand_tilde(path);
|
let path = nu_path::expand_path(path);
|
||||||
path.to_string_lossy().to_string()
|
path.to_string_lossy().to_string()
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
169
crates/nu-command/src/filesystem/cp.rs
Normal file
169
crates/nu-command/src/filesystem/cp.rs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
use std::env::current_dir;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_path::canonicalize_with;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{ShellError, Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
use crate::filesystem::util::FileStructure;
|
||||||
|
|
||||||
|
pub struct Cp;
|
||||||
|
|
||||||
|
impl Command for Cp {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"cp"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Copy files."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("cp")
|
||||||
|
.required("source", SyntaxShape::GlobPattern, "the place to copy from")
|
||||||
|
.required("destination", SyntaxShape::Filepath, "the place to copy to")
|
||||||
|
.switch(
|
||||||
|
"recursive",
|
||||||
|
"copy recursively through subdirectories",
|
||||||
|
Some('r'),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<Value, ShellError> {
|
||||||
|
let source: String = call.req(context, 0)?;
|
||||||
|
let destination: String = call.req(context, 1)?;
|
||||||
|
|
||||||
|
let path: PathBuf = current_dir().unwrap();
|
||||||
|
let source = path.join(source.as_str());
|
||||||
|
let destination = path.join(destination.as_str());
|
||||||
|
|
||||||
|
let sources =
|
||||||
|
glob::glob(&source.to_string_lossy()).map_or_else(|_| Vec::new(), Iterator::collect);
|
||||||
|
if sources.is_empty() {
|
||||||
|
return Err(ShellError::FileNotFound(call.positional[0].span));
|
||||||
|
}
|
||||||
|
|
||||||
|
if sources.len() > 1 && !destination.is_dir() {
|
||||||
|
return Err(ShellError::MoveNotPossible {
|
||||||
|
source_message: "Can't move many files".to_string(),
|
||||||
|
source_span: call.positional[0].span,
|
||||||
|
destination_message: "into single file".to_string(),
|
||||||
|
destination_span: call.positional[1].span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let any_source_is_dir = sources.iter().any(|f| matches!(f, Ok(f) if f.is_dir()));
|
||||||
|
let recursive = call.named.iter().any(|p| &p.0 == "recursive");
|
||||||
|
if any_source_is_dir && !recursive {
|
||||||
|
return Err(ShellError::MoveNotPossibleSingle(
|
||||||
|
"Directories must be copied using \"--recursive\"".to_string(),
|
||||||
|
call.positional[0].span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in sources.into_iter().flatten() {
|
||||||
|
let mut sources = FileStructure::new();
|
||||||
|
sources.walk_decorate(&entry)?;
|
||||||
|
|
||||||
|
if entry.is_file() {
|
||||||
|
let sources = sources.paths_applying_with(|(source_file, _depth_level)| {
|
||||||
|
if destination.is_dir() {
|
||||||
|
let mut dest = canonicalize_with(&destination, &path)?;
|
||||||
|
if let Some(name) = entry.file_name() {
|
||||||
|
dest.push(name);
|
||||||
|
}
|
||||||
|
Ok((source_file, dest))
|
||||||
|
} else {
|
||||||
|
Ok((source_file, destination.clone()))
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for (src, dst) in sources {
|
||||||
|
if src.is_file() {
|
||||||
|
std::fs::copy(&src, dst).map_err(|e| {
|
||||||
|
ShellError::MoveNotPossibleSingle(
|
||||||
|
format!(
|
||||||
|
"failed to move containing file \"{}\": {}",
|
||||||
|
src.to_string_lossy(),
|
||||||
|
e
|
||||||
|
),
|
||||||
|
call.positional[0].span,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if entry.is_dir() {
|
||||||
|
let destination = if !destination.exists() {
|
||||||
|
destination.clone()
|
||||||
|
} else {
|
||||||
|
match entry.file_name() {
|
||||||
|
Some(name) => destination.join(name),
|
||||||
|
None => {
|
||||||
|
return Err(ShellError::FileNotFoundCustom(
|
||||||
|
format!("containing \"{:?}\" is not a valid path", entry),
|
||||||
|
call.positional[0].span,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::fs::create_dir_all(&destination).map_err(|e| {
|
||||||
|
ShellError::MoveNotPossibleSingle(
|
||||||
|
format!("failed to recursively fill destination: {}", e),
|
||||||
|
call.positional[1].span,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let sources = sources.paths_applying_with(|(source_file, depth_level)| {
|
||||||
|
let mut dest = destination.clone();
|
||||||
|
let path = canonicalize_with(&source_file, &path)?;
|
||||||
|
let components = path
|
||||||
|
.components()
|
||||||
|
.map(|fragment| fragment.as_os_str())
|
||||||
|
.rev()
|
||||||
|
.take(1 + depth_level);
|
||||||
|
|
||||||
|
components.for_each(|fragment| dest.push(fragment));
|
||||||
|
Ok((PathBuf::from(&source_file), dest))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for (src, dst) in sources {
|
||||||
|
if src.is_dir() && !dst.exists() {
|
||||||
|
std::fs::create_dir_all(&dst).map_err(|e| {
|
||||||
|
ShellError::MoveNotPossibleSingle(
|
||||||
|
format!(
|
||||||
|
"failed to create containing directory \"{}\": {}",
|
||||||
|
dst.to_string_lossy(),
|
||||||
|
e
|
||||||
|
),
|
||||||
|
call.positional[1].span,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if src.is_file() {
|
||||||
|
std::fs::copy(&src, &dst).map_err(|e| {
|
||||||
|
ShellError::MoveNotPossibleSingle(
|
||||||
|
format!(
|
||||||
|
"failed to move containing file \"{}\": {}",
|
||||||
|
src.to_string_lossy(),
|
||||||
|
e
|
||||||
|
),
|
||||||
|
call.positional[0].span,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Nothing { span: call.head })
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
use chrono::{DateTime, Utc};
|
||||||
use nu_engine::eval_expression;
|
use nu_engine::eval_expression;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
@ -31,7 +32,17 @@ impl Command for Ls {
|
|||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
let pattern = if let Some(expr) = call.positional.get(0) {
|
let pattern = if let Some(expr) = call.positional.get(0) {
|
||||||
let result = eval_expression(context, expr)?;
|
let result = eval_expression(context, expr)?;
|
||||||
result.as_string()?
|
let mut result = result.as_string()?;
|
||||||
|
|
||||||
|
let path = std::path::Path::new(&result);
|
||||||
|
if path.is_dir() {
|
||||||
|
if !result.ends_with(std::path::MAIN_SEPARATOR) {
|
||||||
|
result.push(std::path::MAIN_SEPARATOR);
|
||||||
|
}
|
||||||
|
result.push('*');
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
} else {
|
} else {
|
||||||
"*".into()
|
"*".into()
|
||||||
};
|
};
|
||||||
@ -49,25 +60,39 @@ impl Command for Ls {
|
|||||||
let is_dir = metadata.is_dir();
|
let is_dir = metadata.is_dir();
|
||||||
let filesize = metadata.len();
|
let filesize = metadata.len();
|
||||||
|
|
||||||
|
let mut cols = vec!["name".into(), "type".into(), "size".into()];
|
||||||
|
|
||||||
|
let mut vals = vec![
|
||||||
|
Value::String {
|
||||||
|
val: path.to_string_lossy().to_string(),
|
||||||
|
span: call_span,
|
||||||
|
},
|
||||||
|
if is_file {
|
||||||
|
Value::string("File", call_span)
|
||||||
|
} else if is_dir {
|
||||||
|
Value::string("Dir", call_span)
|
||||||
|
} else {
|
||||||
|
Value::Nothing { span: call_span }
|
||||||
|
},
|
||||||
|
Value::Filesize {
|
||||||
|
val: filesize as i64,
|
||||||
|
span: call_span,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if let Ok(date) = metadata.modified() {
|
||||||
|
let utc: DateTime<Utc> = date.into();
|
||||||
|
|
||||||
|
cols.push("modified".into());
|
||||||
|
vals.push(Value::Date {
|
||||||
|
val: utc.into(),
|
||||||
|
span: call_span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Value::Record {
|
Value::Record {
|
||||||
cols: vec!["name".into(), "type".into(), "size".into()],
|
cols,
|
||||||
vals: vec![
|
vals,
|
||||||
Value::String {
|
|
||||||
val: path.to_string_lossy().to_string(),
|
|
||||||
span: call_span,
|
|
||||||
},
|
|
||||||
if is_file {
|
|
||||||
Value::string("file", call_span)
|
|
||||||
} else if is_dir {
|
|
||||||
Value::string("dir", call_span)
|
|
||||||
} else {
|
|
||||||
Value::Nothing { span: call_span }
|
|
||||||
},
|
|
||||||
Value::Filesize {
|
|
||||||
val: filesize,
|
|
||||||
span: call_span,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
span: call_span,
|
span: call_span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
mod cd;
|
mod cd;
|
||||||
|
mod cp;
|
||||||
mod ls;
|
mod ls;
|
||||||
|
mod mv;
|
||||||
|
mod util;
|
||||||
|
|
||||||
pub use cd::Cd;
|
pub use cd::Cd;
|
||||||
|
pub use cp::Cp;
|
||||||
pub use ls::Ls;
|
pub use ls::Ls;
|
||||||
|
pub use mv::Mv;
|
||||||
|
139
crates/nu-command/src/filesystem/mv.rs
Normal file
139
crates/nu-command/src/filesystem/mv.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
use std::env::current_dir;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{ShellError, Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct Mv;
|
||||||
|
|
||||||
|
impl Command for Mv {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"mv"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Move files or directories."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("mv")
|
||||||
|
.required(
|
||||||
|
"source",
|
||||||
|
SyntaxShape::GlobPattern,
|
||||||
|
"the location to move files/directories from",
|
||||||
|
)
|
||||||
|
.required(
|
||||||
|
"destination",
|
||||||
|
SyntaxShape::Filepath,
|
||||||
|
"the location to move files/directories to",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
// TODO: handle invalid directory or insufficient permissions
|
||||||
|
let source: String = call.req(context, 0)?;
|
||||||
|
let destination: String = call.req(context, 1)?;
|
||||||
|
|
||||||
|
let path: PathBuf = current_dir().unwrap();
|
||||||
|
let source = path.join(source.as_str());
|
||||||
|
let destination = path.join(destination.as_str());
|
||||||
|
|
||||||
|
let mut sources =
|
||||||
|
glob::glob(&source.to_string_lossy()).map_or_else(|_| Vec::new(), Iterator::collect);
|
||||||
|
|
||||||
|
if sources.is_empty() {
|
||||||
|
return Err(ShellError::FileNotFound(
|
||||||
|
call.positional.first().unwrap().span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destination.exists() && !destination.is_dir() && sources.len() > 1)
|
||||||
|
|| (!destination.exists() && sources.len() > 1)
|
||||||
|
{
|
||||||
|
return Err(ShellError::MoveNotPossible {
|
||||||
|
source_message: "Can't move many files".to_string(),
|
||||||
|
source_span: call.positional[0].span,
|
||||||
|
destination_message: "into single file".to_string(),
|
||||||
|
destination_span: call.positional[1].span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let some_if_source_is_destination = sources
|
||||||
|
.iter()
|
||||||
|
.find(|f| matches!(f, Ok(f) if destination.starts_with(f)));
|
||||||
|
if destination.exists() && destination.is_dir() && sources.len() == 1 {
|
||||||
|
if let Some(Ok(_filename)) = some_if_source_is_destination {
|
||||||
|
return Err(ShellError::MoveNotPossible {
|
||||||
|
source_message: "Can't move directory".to_string(),
|
||||||
|
source_span: call.positional[0].span,
|
||||||
|
destination_message: "into itself".to_string(),
|
||||||
|
destination_span: call.positional[1].span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Ok(_filename)) = some_if_source_is_destination {
|
||||||
|
sources = sources
|
||||||
|
.into_iter()
|
||||||
|
.filter(|f| matches!(f, Ok(f) if !destination.starts_with(f)))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in sources.into_iter().flatten() {
|
||||||
|
move_file(call, &entry, &destination)?
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Nothing { span: call.head })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_file(call: &Call, from: &Path, to: &Path) -> Result<(), ShellError> {
|
||||||
|
if to.exists() && from.is_dir() && to.is_file() {
|
||||||
|
return Err(ShellError::MoveNotPossible {
|
||||||
|
source_message: "Can't move a directory".to_string(),
|
||||||
|
source_span: call.positional[0].span,
|
||||||
|
destination_message: "to a file".to_string(),
|
||||||
|
destination_span: call.positional[1].span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let destination_dir_exists = if to.is_dir() {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
to.parent().map(Path::exists).unwrap_or(true)
|
||||||
|
};
|
||||||
|
|
||||||
|
if !destination_dir_exists {
|
||||||
|
return Err(ShellError::DirectoryNotFound(call.positional[1].span));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut to = to.to_path_buf();
|
||||||
|
if to.is_dir() {
|
||||||
|
let from_file_name = match from.file_name() {
|
||||||
|
Some(name) => name,
|
||||||
|
None => return Err(ShellError::DirectoryNotFound(call.positional[1].span)),
|
||||||
|
};
|
||||||
|
|
||||||
|
to.push(from_file_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
move_item(call, from, &to)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_item(call: &Call, from: &Path, to: &Path) -> Result<(), ShellError> {
|
||||||
|
// We first try a rename, which is a quick operation. If that doesn't work, we'll try a copy
|
||||||
|
// and remove the old file/folder. This is necessary if we're moving across filesystems or devices.
|
||||||
|
std::fs::rename(&from, &to).map_err(|_| ShellError::MoveNotPossible {
|
||||||
|
source_message: "failed to move".to_string(),
|
||||||
|
source_span: call.positional[0].span,
|
||||||
|
destination_message: "into".to_string(),
|
||||||
|
destination_span: call.positional[1].span,
|
||||||
|
})
|
||||||
|
}
|
81
crates/nu-command/src/filesystem/util.rs
Normal file
81
crates/nu-command/src/filesystem/util.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use nu_path::canonicalize_with;
|
||||||
|
use nu_protocol::ShellError;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct FileStructure {
|
||||||
|
pub resources: Vec<Resource>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl FileStructure {
|
||||||
|
pub fn new() -> FileStructure {
|
||||||
|
FileStructure { resources: vec![] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains_more_than_one_file(&self) -> bool {
|
||||||
|
self.resources.len() > 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains_files(&self) -> bool {
|
||||||
|
!self.resources.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn paths_applying_with<F>(
|
||||||
|
&mut self,
|
||||||
|
to: F,
|
||||||
|
) -> Result<Vec<(PathBuf, PathBuf)>, Box<dyn std::error::Error>>
|
||||||
|
where
|
||||||
|
F: Fn((PathBuf, usize)) -> Result<(PathBuf, PathBuf), Box<dyn std::error::Error>>,
|
||||||
|
{
|
||||||
|
self.resources
|
||||||
|
.iter()
|
||||||
|
.map(|f| (PathBuf::from(&f.location), f.at))
|
||||||
|
.map(to)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn walk_decorate(&mut self, start_path: &Path) -> Result<(), ShellError> {
|
||||||
|
self.resources = Vec::<Resource>::new();
|
||||||
|
self.build(start_path, 0)?;
|
||||||
|
self.resources.sort();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build(&mut self, src: &Path, lvl: usize) -> Result<(), ShellError> {
|
||||||
|
let source = canonicalize_with(src, std::env::current_dir()?)?;
|
||||||
|
|
||||||
|
if source.is_dir() {
|
||||||
|
for entry in std::fs::read_dir(src)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let path = entry.path();
|
||||||
|
|
||||||
|
if path.is_dir() {
|
||||||
|
self.build(&path, lvl + 1)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.resources.push(Resource {
|
||||||
|
location: path.to_path_buf(),
|
||||||
|
at: lvl,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.resources.push(Resource {
|
||||||
|
location: source,
|
||||||
|
at: lvl,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub struct Resource {
|
||||||
|
pub at: usize,
|
||||||
|
pub location: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource {}
|
@ -15,7 +15,7 @@ impl Command for Get {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("wrap").required(
|
Signature::build("get").required(
|
||||||
"cell_path",
|
"cell_path",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"the cell path to the data",
|
"the cell path to the data",
|
||||||
|
@ -86,13 +86,13 @@ fn run_ps(call: &Call) -> Result<Value, ShellError> {
|
|||||||
|
|
||||||
cols.push("mem".into());
|
cols.push("mem".into());
|
||||||
vals.push(Value::Filesize {
|
vals.push(Value::Filesize {
|
||||||
val: result.memory() * 1000,
|
val: result.memory() as i64 * 1000,
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
cols.push("virtual".into());
|
cols.push("virtual".into());
|
||||||
vals.push(Value::Filesize {
|
vals.push(Value::Filesize {
|
||||||
val: result.virtual_memory() * 1000,
|
val: result.virtual_memory() as i64 * 1000,
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -112,13 +112,13 @@ pub fn disks(sys: &mut System, span: Span) -> Option<Value> {
|
|||||||
|
|
||||||
cols.push("total".into());
|
cols.push("total".into());
|
||||||
vals.push(Value::Filesize {
|
vals.push(Value::Filesize {
|
||||||
val: disk.total_space(),
|
val: disk.total_space() as i64,
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
cols.push("free".into());
|
cols.push("free".into());
|
||||||
vals.push(Value::Filesize {
|
vals.push(Value::Filesize {
|
||||||
val: disk.available_space(),
|
val: disk.available_space() as i64,
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -148,13 +148,13 @@ pub fn net(sys: &mut System, span: Span) -> Option<Value> {
|
|||||||
|
|
||||||
cols.push("sent".into());
|
cols.push("sent".into());
|
||||||
vals.push(Value::Filesize {
|
vals.push(Value::Filesize {
|
||||||
val: data.total_transmitted(),
|
val: data.total_transmitted() as i64,
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
cols.push("recv".into());
|
cols.push("recv".into());
|
||||||
vals.push(Value::Filesize {
|
vals.push(Value::Filesize {
|
||||||
val: data.total_received(),
|
val: data.total_received() as i64,
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -215,25 +215,25 @@ pub fn mem(sys: &mut System, span: Span) -> Option<Value> {
|
|||||||
|
|
||||||
cols.push("total".into());
|
cols.push("total".into());
|
||||||
vals.push(Value::Filesize {
|
vals.push(Value::Filesize {
|
||||||
val: total_mem * 1000,
|
val: total_mem as i64 * 1000,
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
cols.push("free".into());
|
cols.push("free".into());
|
||||||
vals.push(Value::Filesize {
|
vals.push(Value::Filesize {
|
||||||
val: free_mem * 1000,
|
val: free_mem as i64 * 1000,
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
cols.push("swap total".into());
|
cols.push("swap total".into());
|
||||||
vals.push(Value::Filesize {
|
vals.push(Value::Filesize {
|
||||||
val: total_swap * 1000,
|
val: total_swap as i64 * 1000,
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
cols.push("swap free".into());
|
cols.push("swap free".into());
|
||||||
vals.push(Value::Filesize {
|
vals.push(Value::Filesize {
|
||||||
val: free_swap * 1000,
|
val: free_swap as i64 * 1000,
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -276,7 +276,7 @@ pub fn host(sys: &mut System, span: Span) -> Option<Value> {
|
|||||||
}
|
}
|
||||||
cols.push("uptime".into());
|
cols.push("uptime".into());
|
||||||
vals.push(Value::Duration {
|
vals.push(Value::Duration {
|
||||||
val: 1000000000 * sys.uptime() as u64,
|
val: 1000000000 * sys.uptime() as i64,
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::ast::{Block, Call, Expr, Expression, Operator, Statement};
|
use nu_protocol::ast::{Block, Call, Expr, Expression, Operator, Statement};
|
||||||
use nu_protocol::engine::EvaluationContext;
|
use nu_protocol::engine::EvaluationContext;
|
||||||
use nu_protocol::{Range, ShellError, Span, Type, Value};
|
use nu_protocol::{Range, ShellError, Span, Type, Unit, Value};
|
||||||
|
|
||||||
pub fn eval_operator(op: &Expression) -> Result<Operator, ShellError> {
|
pub fn eval_operator(op: &Expression) -> Result<Operator, ShellError> {
|
||||||
match op {
|
match op {
|
||||||
@ -124,6 +124,10 @@ pub fn eval_expression(
|
|||||||
val: *f,
|
val: *f,
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
}),
|
}),
|
||||||
|
Expr::ValueWithUnit(e, unit) => match eval_expression(context, e)? {
|
||||||
|
Value::Int { val, .. } => Ok(compute(val, unit.item, unit.span)),
|
||||||
|
_ => Err(ShellError::CantConvert("unit value".into(), e.span)),
|
||||||
|
},
|
||||||
Expr::Range(from, next, to, operator) => {
|
Expr::Range(from, next, to, operator) => {
|
||||||
let from = if let Some(f) = from {
|
let from = if let Some(f) = from {
|
||||||
eval_expression(context, f)?
|
eval_expression(context, f)?
|
||||||
@ -241,6 +245,14 @@ pub fn eval_expression(
|
|||||||
val: s.clone(),
|
val: s.clone(),
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
}),
|
}),
|
||||||
|
Expr::Filepath(s) => Ok(Value::String {
|
||||||
|
val: s.clone(),
|
||||||
|
span: expr.span,
|
||||||
|
}),
|
||||||
|
Expr::GlobPattern(s) => Ok(Value::String {
|
||||||
|
val: s.clone(),
|
||||||
|
span: expr.span,
|
||||||
|
}),
|
||||||
Expr::Signature(_) => Ok(Value::Nothing { span: expr.span }),
|
Expr::Signature(_) => Ok(Value::Nothing { span: expr.span }),
|
||||||
Expr::Garbage => Ok(Value::Nothing { span: expr.span }),
|
Expr::Garbage => Ok(Value::Nothing { span: expr.span }),
|
||||||
}
|
}
|
||||||
@ -285,14 +297,79 @@ pub fn eval_block(
|
|||||||
Ok(input)
|
Ok(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn eval(context: &EvaluationContext, script: &str) -> Result<Value, ShellError> {
|
pub fn compute(size: i64, unit: Unit, span: Span) -> Value {
|
||||||
// let engine_state = EngineState::new();
|
match unit {
|
||||||
// let mut working_set = StateWorkingSet::new(&engine_state);
|
Unit::Byte => Value::Filesize { val: size, span },
|
||||||
|
Unit::Kilobyte => Value::Filesize {
|
||||||
|
val: size * 1000,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Unit::Megabyte => Value::Filesize {
|
||||||
|
val: size * 1000 * 1000,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Unit::Gigabyte => Value::Filesize {
|
||||||
|
val: size * 1000 * 1000 * 1000,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Unit::Terabyte => Value::Filesize {
|
||||||
|
val: size * 1000 * 1000 * 1000 * 1000,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Unit::Petabyte => Value::Filesize {
|
||||||
|
val: size * 1000 * 1000 * 1000 * 1000 * 1000,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
|
||||||
// let (block, err) = parse(&mut working_set, None, b"3", true);
|
Unit::Kibibyte => Value::Filesize {
|
||||||
// if let Some(e) = err {
|
val: size * 1024,
|
||||||
// Err(e)
|
span,
|
||||||
// } else {
|
},
|
||||||
// eval_block(context, &block, Value::nothing())
|
Unit::Mebibyte => Value::Filesize {
|
||||||
// }
|
val: size * 1024 * 1024,
|
||||||
// }
|
span,
|
||||||
|
},
|
||||||
|
Unit::Gibibyte => Value::Filesize {
|
||||||
|
val: size * 1024 * 1024 * 1024,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Unit::Tebibyte => Value::Filesize {
|
||||||
|
val: size * 1024 * 1024 * 1024 * 1024,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Unit::Pebibyte => Value::Filesize {
|
||||||
|
val: size * 1024 * 1024 * 1024 * 1024 * 1024,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
|
||||||
|
Unit::Nanosecond => Value::Duration { val: size, span },
|
||||||
|
Unit::Microsecond => Value::Duration {
|
||||||
|
val: size * 1000,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Unit::Millisecond => Value::Duration {
|
||||||
|
val: size * 1000 * 1000,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Unit::Second => Value::Duration {
|
||||||
|
val: size * 1000 * 1000 * 1000,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Unit::Minute => Value::Duration {
|
||||||
|
val: size * 1000 * 1000 * 1000 * 60,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Unit::Hour => Value::Duration {
|
||||||
|
val: size * 1000 * 1000 * 1000 * 60 * 60,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Unit::Day => Value::Duration {
|
||||||
|
val: size * 1000 * 1000 * 1000 * 60 * 60 * 24,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Unit::Week => Value::Duration {
|
||||||
|
val: size * 1000 * 1000 * 1000 * 60 * 60 * 24 * 7,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -15,6 +15,8 @@ pub enum FlatShape {
|
|||||||
Operator,
|
Operator,
|
||||||
Signature,
|
Signature,
|
||||||
String,
|
String,
|
||||||
|
Filepath,
|
||||||
|
GlobPattern,
|
||||||
Variable,
|
Variable,
|
||||||
Custom(String),
|
Custom(String),
|
||||||
}
|
}
|
||||||
@ -79,6 +81,12 @@ pub fn flatten_expression(
|
|||||||
Expr::Float(_) => {
|
Expr::Float(_) => {
|
||||||
vec![(expr.span, FlatShape::Float)]
|
vec![(expr.span, FlatShape::Float)]
|
||||||
}
|
}
|
||||||
|
Expr::ValueWithUnit(x, unit) => {
|
||||||
|
let mut output = flatten_expression(working_set, x);
|
||||||
|
output.push((unit.span, FlatShape::String));
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
Expr::CellPath(cell_path) => {
|
Expr::CellPath(cell_path) => {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
for path_element in &cell_path.members {
|
for path_element in &cell_path.members {
|
||||||
@ -118,7 +126,12 @@ pub fn flatten_expression(
|
|||||||
Expr::Bool(_) => {
|
Expr::Bool(_) => {
|
||||||
vec![(expr.span, FlatShape::Bool)]
|
vec![(expr.span, FlatShape::Bool)]
|
||||||
}
|
}
|
||||||
|
Expr::Filepath(_) => {
|
||||||
|
vec![(expr.span, FlatShape::Filepath)]
|
||||||
|
}
|
||||||
|
Expr::GlobPattern(_) => {
|
||||||
|
vec![(expr.span, FlatShape::GlobPattern)]
|
||||||
|
}
|
||||||
Expr::List(list) => {
|
Expr::List(list) => {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
for l in list {
|
for l in list {
|
||||||
|
@ -601,12 +601,90 @@ pub fn parse_hide(
|
|||||||
let (name_expr, err) = parse_string(working_set, spans[1]);
|
let (name_expr, err) = parse_string(working_set, spans[1]);
|
||||||
error = error.or(err);
|
error = error.or(err);
|
||||||
|
|
||||||
let name_bytes: Vec<u8> = working_set.get_span_contents(spans[1]).into();
|
let (import_pattern, err) = parse_import_pattern(working_set, spans[1]);
|
||||||
|
error = error.or(err);
|
||||||
|
|
||||||
// TODO: Do the import pattern stuff for bulk-hiding
|
let exported_names: Vec<Vec<u8>> =
|
||||||
|
if let Some(block_id) = working_set.find_module(&import_pattern.head) {
|
||||||
|
working_set
|
||||||
|
.get_block(block_id)
|
||||||
|
.exports
|
||||||
|
.iter()
|
||||||
|
.map(|(name, _)| name.clone())
|
||||||
|
.collect()
|
||||||
|
} else if import_pattern.members.is_empty() {
|
||||||
|
// The pattern head can be e.g. a function name, not just a module
|
||||||
|
vec![import_pattern.head.clone()]
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
garbage_statement(spans),
|
||||||
|
Some(ParseError::ModuleNotFound(spans[1])),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
if working_set.hide_decl(&name_bytes).is_none() {
|
// This kind of inverts the import pattern matching found in parse_use()
|
||||||
error = error.or_else(|| Some(ParseError::UnknownCommand(spans[1])));
|
let names_to_hide = if import_pattern.members.is_empty() {
|
||||||
|
exported_names
|
||||||
|
} else {
|
||||||
|
match &import_pattern.members[0] {
|
||||||
|
ImportPatternMember::Glob { .. } => exported_names
|
||||||
|
.into_iter()
|
||||||
|
.map(|name| {
|
||||||
|
let mut new_name = import_pattern.head.to_vec();
|
||||||
|
new_name.push(b'.');
|
||||||
|
new_name.extend(&name);
|
||||||
|
new_name
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
ImportPatternMember::Name { name, span } => {
|
||||||
|
let new_exports: Vec<Vec<u8>> = exported_names
|
||||||
|
.into_iter()
|
||||||
|
.filter(|n| n == name)
|
||||||
|
.map(|n| {
|
||||||
|
let mut new_name = import_pattern.head.to_vec();
|
||||||
|
new_name.push(b'.');
|
||||||
|
new_name.extend(&n);
|
||||||
|
new_name
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if new_exports.is_empty() {
|
||||||
|
error = error.or(Some(ParseError::ExportNotFound(*span)))
|
||||||
|
}
|
||||||
|
|
||||||
|
new_exports
|
||||||
|
}
|
||||||
|
ImportPatternMember::List { names } => {
|
||||||
|
let mut output = vec![];
|
||||||
|
|
||||||
|
for (name, span) in names {
|
||||||
|
let mut new_exports: Vec<Vec<u8>> = exported_names
|
||||||
|
.iter()
|
||||||
|
.filter_map(|n| if n == name { Some(n.clone()) } else { None })
|
||||||
|
.map(|n| {
|
||||||
|
let mut new_name = import_pattern.head.to_vec();
|
||||||
|
new_name.push(b'.');
|
||||||
|
new_name.extend(n);
|
||||||
|
new_name
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if new_exports.is_empty() {
|
||||||
|
error = error.or(Some(ParseError::ExportNotFound(*span)))
|
||||||
|
} else {
|
||||||
|
output.append(&mut new_exports)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for name in names_to_hide {
|
||||||
|
if working_set.hide_decl(&name).is_none() {
|
||||||
|
error = error.or_else(|| Some(ParseError::UnknownCommand(spans[1])));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the Hide command call
|
// Create the Hide command call
|
||||||
|
@ -11,7 +11,7 @@ use nu_protocol::{
|
|||||||
Operator, PathMember, Pipeline, RangeInclusion, RangeOperator, Statement,
|
Operator, PathMember, Pipeline, RangeInclusion, RangeOperator, Statement,
|
||||||
},
|
},
|
||||||
engine::StateWorkingSet,
|
engine::StateWorkingSet,
|
||||||
span, Flag, PositionalArg, Signature, Span, SyntaxShape, Type, VarId,
|
span, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::parse_keywords::{
|
use crate::parse_keywords::{
|
||||||
@ -1321,6 +1321,255 @@ pub fn parse_full_cell_path(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_filepath(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
span: Span,
|
||||||
|
) -> (Expression, Option<ParseError>) {
|
||||||
|
let bytes = working_set.get_span_contents(span);
|
||||||
|
let bytes = if (bytes.starts_with(b"\"") && bytes.ends_with(b"\"") && bytes.len() > 1)
|
||||||
|
|| (bytes.starts_with(b"\'") && bytes.ends_with(b"\'") && bytes.len() > 1)
|
||||||
|
{
|
||||||
|
&bytes[1..(bytes.len() - 1)]
|
||||||
|
} else {
|
||||||
|
bytes
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(token) = String::from_utf8(bytes.into()) {
|
||||||
|
(
|
||||||
|
Expression {
|
||||||
|
expr: Expr::Filepath(token),
|
||||||
|
span,
|
||||||
|
ty: Type::String,
|
||||||
|
custom_completion: None,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
garbage(span),
|
||||||
|
Some(ParseError::Expected("string".into(), span)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a duration type, eg '10day'
|
||||||
|
pub fn parse_duration(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
span: Span,
|
||||||
|
) -> (Expression, Option<ParseError>) {
|
||||||
|
fn parse_decimal_str_to_number(decimal: &str) -> Option<i64> {
|
||||||
|
let string_to_parse = format!("0.{}", decimal);
|
||||||
|
if let Ok(x) = string_to_parse.parse::<f64>() {
|
||||||
|
return Some((1_f64 / x) as i64);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
let bytes = working_set.get_span_contents(span);
|
||||||
|
let token = String::from_utf8_lossy(bytes).to_string();
|
||||||
|
|
||||||
|
let unit_groups = [
|
||||||
|
(Unit::Nanosecond, "NS", None),
|
||||||
|
(Unit::Microsecond, "US", Some((Unit::Nanosecond, 1000))),
|
||||||
|
(Unit::Millisecond, "MS", Some((Unit::Microsecond, 1000))),
|
||||||
|
(Unit::Second, "SEC", Some((Unit::Millisecond, 1000))),
|
||||||
|
(Unit::Minute, "MIN", Some((Unit::Second, 60))),
|
||||||
|
(Unit::Hour, "HR", Some((Unit::Minute, 60))),
|
||||||
|
(Unit::Day, "DAY", Some((Unit::Minute, 1440))),
|
||||||
|
(Unit::Week, "WK", Some((Unit::Day, 7))),
|
||||||
|
];
|
||||||
|
if let Some(unit) = unit_groups
|
||||||
|
.iter()
|
||||||
|
.find(|&x| token.to_uppercase().ends_with(x.1))
|
||||||
|
{
|
||||||
|
let mut lhs = token.clone();
|
||||||
|
for _ in 0..unit.1.len() {
|
||||||
|
lhs.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
let input: Vec<&str> = lhs.split('.').collect();
|
||||||
|
let (value, unit_to_use) = match &input[..] {
|
||||||
|
[number_str] => (number_str.parse::<i64>().ok(), unit.0),
|
||||||
|
[number_str, decimal_part_str] => match unit.2 {
|
||||||
|
Some(unit_to_convert_to) => match (
|
||||||
|
number_str.parse::<i64>(),
|
||||||
|
parse_decimal_str_to_number(decimal_part_str),
|
||||||
|
) {
|
||||||
|
(Ok(number), Some(decimal_part)) => (
|
||||||
|
Some(
|
||||||
|
(number * unit_to_convert_to.1) + (unit_to_convert_to.1 / decimal_part),
|
||||||
|
),
|
||||||
|
unit_to_convert_to.0,
|
||||||
|
),
|
||||||
|
_ => (None, unit.0),
|
||||||
|
},
|
||||||
|
None => (None, unit.0),
|
||||||
|
},
|
||||||
|
_ => (None, unit.0),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(x) = value {
|
||||||
|
let lhs_span = Span::new(span.start, span.start + lhs.len());
|
||||||
|
let unit_span = Span::new(span.start + lhs.len(), span.end);
|
||||||
|
return (
|
||||||
|
Expression {
|
||||||
|
expr: Expr::ValueWithUnit(
|
||||||
|
Box::new(Expression {
|
||||||
|
expr: Expr::Int(x),
|
||||||
|
span: lhs_span,
|
||||||
|
ty: Type::Number,
|
||||||
|
custom_completion: None,
|
||||||
|
}),
|
||||||
|
Spanned {
|
||||||
|
item: unit_to_use,
|
||||||
|
span: unit_span,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
ty: Type::Duration,
|
||||||
|
custom_completion: None,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
garbage(span),
|
||||||
|
Some(ParseError::Mismatch(
|
||||||
|
"duration".into(),
|
||||||
|
"non-duration unit".into(),
|
||||||
|
span,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a unit type, eg '10kb'
|
||||||
|
pub fn parse_filesize(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
span: Span,
|
||||||
|
) -> (Expression, Option<ParseError>) {
|
||||||
|
fn parse_decimal_str_to_number(decimal: &str) -> Option<i64> {
|
||||||
|
let string_to_parse = format!("0.{}", decimal);
|
||||||
|
if let Ok(x) = string_to_parse.parse::<f64>() {
|
||||||
|
return Some((1_f64 / x) as i64);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
let bytes = working_set.get_span_contents(span);
|
||||||
|
let token = String::from_utf8_lossy(bytes).to_string();
|
||||||
|
|
||||||
|
let unit_groups = [
|
||||||
|
(Unit::Kilobyte, "KB", Some((Unit::Byte, 1000))),
|
||||||
|
(Unit::Megabyte, "MB", Some((Unit::Kilobyte, 1000))),
|
||||||
|
(Unit::Gigabyte, "GB", Some((Unit::Megabyte, 1000))),
|
||||||
|
(Unit::Terabyte, "TB", Some((Unit::Gigabyte, 1000))),
|
||||||
|
(Unit::Petabyte, "PB", Some((Unit::Terabyte, 1000))),
|
||||||
|
(Unit::Kibibyte, "KIB", Some((Unit::Byte, 1024))),
|
||||||
|
(Unit::Mebibyte, "MIB", Some((Unit::Kibibyte, 1024))),
|
||||||
|
(Unit::Gibibyte, "GIB", Some((Unit::Mebibyte, 1024))),
|
||||||
|
(Unit::Tebibyte, "TIB", Some((Unit::Gibibyte, 1024))),
|
||||||
|
(Unit::Pebibyte, "PIB", Some((Unit::Tebibyte, 1024))),
|
||||||
|
(Unit::Byte, "B", None),
|
||||||
|
];
|
||||||
|
if let Some(unit) = unit_groups
|
||||||
|
.iter()
|
||||||
|
.find(|&x| token.to_uppercase().ends_with(x.1))
|
||||||
|
{
|
||||||
|
let mut lhs = token.clone();
|
||||||
|
for _ in 0..unit.1.len() {
|
||||||
|
lhs.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
let input: Vec<&str> = lhs.split('.').collect();
|
||||||
|
let (value, unit_to_use) = match &input[..] {
|
||||||
|
[number_str] => (number_str.parse::<i64>().ok(), unit.0),
|
||||||
|
[number_str, decimal_part_str] => match unit.2 {
|
||||||
|
Some(unit_to_convert_to) => match (
|
||||||
|
number_str.parse::<i64>(),
|
||||||
|
parse_decimal_str_to_number(decimal_part_str),
|
||||||
|
) {
|
||||||
|
(Ok(number), Some(decimal_part)) => (
|
||||||
|
Some(
|
||||||
|
(number * unit_to_convert_to.1) + (unit_to_convert_to.1 / decimal_part),
|
||||||
|
),
|
||||||
|
unit_to_convert_to.0,
|
||||||
|
),
|
||||||
|
_ => (None, unit.0),
|
||||||
|
},
|
||||||
|
None => (None, unit.0),
|
||||||
|
},
|
||||||
|
_ => (None, unit.0),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(x) = value {
|
||||||
|
let lhs_span = Span::new(span.start, span.start + lhs.len());
|
||||||
|
let unit_span = Span::new(span.start + lhs.len(), span.end);
|
||||||
|
return (
|
||||||
|
Expression {
|
||||||
|
expr: Expr::ValueWithUnit(
|
||||||
|
Box::new(Expression {
|
||||||
|
expr: Expr::Int(x),
|
||||||
|
span: lhs_span,
|
||||||
|
ty: Type::Number,
|
||||||
|
custom_completion: None,
|
||||||
|
}),
|
||||||
|
Spanned {
|
||||||
|
item: unit_to_use,
|
||||||
|
span: unit_span,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
ty: Type::Filesize,
|
||||||
|
custom_completion: None,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
garbage(span),
|
||||||
|
Some(ParseError::Mismatch(
|
||||||
|
"filesize".into(),
|
||||||
|
"non-filesize unit".into(),
|
||||||
|
span,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_glob_pattern(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
span: Span,
|
||||||
|
) -> (Expression, Option<ParseError>) {
|
||||||
|
let bytes = working_set.get_span_contents(span);
|
||||||
|
let bytes = if (bytes.starts_with(b"\"") && bytes.ends_with(b"\"") && bytes.len() > 1)
|
||||||
|
|| (bytes.starts_with(b"\'") && bytes.ends_with(b"\'") && bytes.len() > 1)
|
||||||
|
{
|
||||||
|
&bytes[1..(bytes.len() - 1)]
|
||||||
|
} else {
|
||||||
|
bytes
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(token) = String::from_utf8(bytes.into()) {
|
||||||
|
(
|
||||||
|
Expression {
|
||||||
|
expr: Expr::GlobPattern(token),
|
||||||
|
span,
|
||||||
|
ty: Type::String,
|
||||||
|
custom_completion: None,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
garbage(span),
|
||||||
|
Some(ParseError::Expected("string".into(), span)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_string(
|
pub fn parse_string(
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
span: Span,
|
span: Span,
|
||||||
@ -1365,7 +1614,7 @@ pub fn parse_shape_name(
|
|||||||
b"number" => SyntaxShape::Number,
|
b"number" => SyntaxShape::Number,
|
||||||
b"range" => SyntaxShape::Range,
|
b"range" => SyntaxShape::Range,
|
||||||
b"int" => SyntaxShape::Int,
|
b"int" => SyntaxShape::Int,
|
||||||
b"path" => SyntaxShape::FilePath,
|
b"path" => SyntaxShape::Filepath,
|
||||||
b"glob" => SyntaxShape::GlobPattern,
|
b"glob" => SyntaxShape::GlobPattern,
|
||||||
b"block" => SyntaxShape::Block(None), //FIXME
|
b"block" => SyntaxShape::Block(None), //FIXME
|
||||||
b"cond" => SyntaxShape::RowCondition,
|
b"cond" => SyntaxShape::RowCondition,
|
||||||
@ -2320,10 +2569,12 @@ pub fn parse_value(
|
|||||||
}
|
}
|
||||||
SyntaxShape::Number => parse_number(bytes, span),
|
SyntaxShape::Number => parse_number(bytes, span),
|
||||||
SyntaxShape::Int => parse_int(bytes, span),
|
SyntaxShape::Int => parse_int(bytes, span),
|
||||||
|
SyntaxShape::Duration => parse_duration(working_set, span),
|
||||||
|
SyntaxShape::Filesize => parse_filesize(working_set, span),
|
||||||
SyntaxShape::Range => parse_range(working_set, span),
|
SyntaxShape::Range => parse_range(working_set, span),
|
||||||
SyntaxShape::String | SyntaxShape::GlobPattern | SyntaxShape::FilePath => {
|
SyntaxShape::Filepath => parse_filepath(working_set, span),
|
||||||
parse_string(working_set, span)
|
SyntaxShape::GlobPattern => parse_glob_pattern(working_set, span),
|
||||||
}
|
SyntaxShape::String => parse_string(working_set, span),
|
||||||
SyntaxShape::Block(_) => {
|
SyntaxShape::Block(_) => {
|
||||||
if bytes.starts_with(b"{") {
|
if bytes.starts_with(b"{") {
|
||||||
parse_block_expression(working_set, shape, span)
|
parse_block_expression(working_set, shape, span)
|
||||||
|
@ -29,6 +29,9 @@ pub fn math_result_type(
|
|||||||
(Type::Int, Type::Float) => (Type::Float, None),
|
(Type::Int, Type::Float) => (Type::Float, None),
|
||||||
(Type::Float, Type::Float) => (Type::Float, None),
|
(Type::Float, Type::Float) => (Type::Float, None),
|
||||||
(Type::String, Type::String) => (Type::String, None),
|
(Type::String, Type::String) => (Type::String, None),
|
||||||
|
(Type::Duration, Type::Duration) => (Type::Duration, None),
|
||||||
|
(Type::Filesize, Type::Filesize) => (Type::Filesize, None),
|
||||||
|
|
||||||
(Type::Unknown, _) => (Type::Unknown, None),
|
(Type::Unknown, _) => (Type::Unknown, None),
|
||||||
(_, Type::Unknown) => (Type::Unknown, None),
|
(_, Type::Unknown) => (Type::Unknown, None),
|
||||||
(Type::Int, _) => {
|
(Type::Int, _) => {
|
||||||
@ -64,6 +67,9 @@ pub fn math_result_type(
|
|||||||
(Type::Float, Type::Int) => (Type::Float, None),
|
(Type::Float, Type::Int) => (Type::Float, None),
|
||||||
(Type::Int, Type::Float) => (Type::Float, None),
|
(Type::Int, Type::Float) => (Type::Float, None),
|
||||||
(Type::Float, Type::Float) => (Type::Float, None),
|
(Type::Float, Type::Float) => (Type::Float, None),
|
||||||
|
(Type::Duration, Type::Duration) => (Type::Duration, None),
|
||||||
|
(Type::Filesize, Type::Filesize) => (Type::Filesize, None),
|
||||||
|
|
||||||
(Type::Unknown, _) => (Type::Unknown, None),
|
(Type::Unknown, _) => (Type::Unknown, None),
|
||||||
(_, Type::Unknown) => (Type::Unknown, None),
|
(_, Type::Unknown) => (Type::Unknown, None),
|
||||||
_ => {
|
_ => {
|
||||||
@ -85,6 +91,7 @@ pub fn math_result_type(
|
|||||||
(Type::Float, Type::Int) => (Type::Float, None),
|
(Type::Float, Type::Int) => (Type::Float, None),
|
||||||
(Type::Int, Type::Float) => (Type::Float, None),
|
(Type::Int, Type::Float) => (Type::Float, None),
|
||||||
(Type::Float, Type::Float) => (Type::Float, None),
|
(Type::Float, Type::Float) => (Type::Float, None),
|
||||||
|
|
||||||
(Type::Unknown, _) => (Type::Unknown, None),
|
(Type::Unknown, _) => (Type::Unknown, None),
|
||||||
(_, Type::Unknown) => (Type::Unknown, None),
|
(_, Type::Unknown) => (Type::Unknown, None),
|
||||||
_ => {
|
_ => {
|
||||||
@ -106,6 +113,7 @@ pub fn math_result_type(
|
|||||||
(Type::Float, Type::Int) => (Type::Float, None),
|
(Type::Float, Type::Int) => (Type::Float, None),
|
||||||
(Type::Int, Type::Float) => (Type::Float, None),
|
(Type::Int, Type::Float) => (Type::Float, None),
|
||||||
(Type::Float, Type::Float) => (Type::Float, None),
|
(Type::Float, Type::Float) => (Type::Float, None),
|
||||||
|
|
||||||
(Type::Unknown, _) => (Type::Unknown, None),
|
(Type::Unknown, _) => (Type::Unknown, None),
|
||||||
(_, Type::Unknown) => (Type::Unknown, None),
|
(_, Type::Unknown) => (Type::Unknown, None),
|
||||||
_ => {
|
_ => {
|
||||||
@ -127,6 +135,9 @@ pub fn math_result_type(
|
|||||||
(Type::Float, Type::Int) => (Type::Bool, None),
|
(Type::Float, Type::Int) => (Type::Bool, None),
|
||||||
(Type::Int, Type::Float) => (Type::Bool, None),
|
(Type::Int, Type::Float) => (Type::Bool, None),
|
||||||
(Type::Float, Type::Float) => (Type::Bool, None),
|
(Type::Float, Type::Float) => (Type::Bool, None),
|
||||||
|
(Type::Duration, Type::Duration) => (Type::Bool, None),
|
||||||
|
(Type::Filesize, Type::Filesize) => (Type::Bool, None),
|
||||||
|
|
||||||
(Type::Unknown, _) => (Type::Bool, None),
|
(Type::Unknown, _) => (Type::Bool, None),
|
||||||
(_, Type::Unknown) => (Type::Bool, None),
|
(_, Type::Unknown) => (Type::Bool, None),
|
||||||
_ => {
|
_ => {
|
||||||
@ -148,6 +159,9 @@ pub fn math_result_type(
|
|||||||
(Type::Float, Type::Int) => (Type::Bool, None),
|
(Type::Float, Type::Int) => (Type::Bool, None),
|
||||||
(Type::Int, Type::Float) => (Type::Bool, None),
|
(Type::Int, Type::Float) => (Type::Bool, None),
|
||||||
(Type::Float, Type::Float) => (Type::Bool, None),
|
(Type::Float, Type::Float) => (Type::Bool, None),
|
||||||
|
(Type::Duration, Type::Duration) => (Type::Bool, None),
|
||||||
|
(Type::Filesize, Type::Filesize) => (Type::Bool, None),
|
||||||
|
|
||||||
(Type::Unknown, _) => (Type::Bool, None),
|
(Type::Unknown, _) => (Type::Bool, None),
|
||||||
(_, Type::Unknown) => (Type::Bool, None),
|
(_, Type::Unknown) => (Type::Bool, None),
|
||||||
_ => {
|
_ => {
|
||||||
@ -169,6 +183,9 @@ pub fn math_result_type(
|
|||||||
(Type::Float, Type::Int) => (Type::Bool, None),
|
(Type::Float, Type::Int) => (Type::Bool, None),
|
||||||
(Type::Int, Type::Float) => (Type::Bool, None),
|
(Type::Int, Type::Float) => (Type::Bool, None),
|
||||||
(Type::Float, Type::Float) => (Type::Bool, None),
|
(Type::Float, Type::Float) => (Type::Bool, None),
|
||||||
|
(Type::Duration, Type::Duration) => (Type::Bool, None),
|
||||||
|
(Type::Filesize, Type::Filesize) => (Type::Bool, None),
|
||||||
|
|
||||||
(Type::Unknown, _) => (Type::Bool, None),
|
(Type::Unknown, _) => (Type::Bool, None),
|
||||||
(_, Type::Unknown) => (Type::Bool, None),
|
(_, Type::Unknown) => (Type::Bool, None),
|
||||||
_ => {
|
_ => {
|
||||||
@ -190,6 +207,9 @@ pub fn math_result_type(
|
|||||||
(Type::Float, Type::Int) => (Type::Bool, None),
|
(Type::Float, Type::Int) => (Type::Bool, None),
|
||||||
(Type::Int, Type::Float) => (Type::Bool, None),
|
(Type::Int, Type::Float) => (Type::Bool, None),
|
||||||
(Type::Float, Type::Float) => (Type::Bool, None),
|
(Type::Float, Type::Float) => (Type::Bool, None),
|
||||||
|
(Type::Duration, Type::Duration) => (Type::Bool, None),
|
||||||
|
(Type::Filesize, Type::Filesize) => (Type::Bool, None),
|
||||||
|
|
||||||
(Type::Unknown, _) => (Type::Bool, None),
|
(Type::Unknown, _) => (Type::Bool, None),
|
||||||
(_, Type::Unknown) => (Type::Bool, None),
|
(_, Type::Unknown) => (Type::Bool, None),
|
||||||
_ => {
|
_ => {
|
||||||
@ -209,6 +229,9 @@ pub fn math_result_type(
|
|||||||
Operator::Equal => match (&lhs.ty, &rhs.ty) {
|
Operator::Equal => match (&lhs.ty, &rhs.ty) {
|
||||||
(Type::Float, Type::Int) => (Type::Bool, None),
|
(Type::Float, Type::Int) => (Type::Bool, None),
|
||||||
(Type::Int, Type::Float) => (Type::Bool, None),
|
(Type::Int, Type::Float) => (Type::Bool, None),
|
||||||
|
(Type::Duration, Type::Duration) => (Type::Bool, None),
|
||||||
|
(Type::Filesize, Type::Filesize) => (Type::Bool, None),
|
||||||
|
|
||||||
(x, y) if x == y => (Type::Bool, None),
|
(x, y) if x == y => (Type::Bool, None),
|
||||||
(Type::Unknown, _) => (Type::Bool, None),
|
(Type::Unknown, _) => (Type::Bool, None),
|
||||||
(_, Type::Unknown) => (Type::Bool, None),
|
(_, Type::Unknown) => (Type::Bool, None),
|
||||||
@ -231,6 +254,9 @@ pub fn math_result_type(
|
|||||||
(Type::Float, Type::Int) => (Type::Bool, None),
|
(Type::Float, Type::Int) => (Type::Bool, None),
|
||||||
(Type::Int, Type::Float) => (Type::Bool, None),
|
(Type::Int, Type::Float) => (Type::Bool, None),
|
||||||
(Type::Float, Type::Float) => (Type::Bool, None),
|
(Type::Float, Type::Float) => (Type::Bool, None),
|
||||||
|
(Type::Duration, Type::Duration) => (Type::Bool, None),
|
||||||
|
(Type::Filesize, Type::Filesize) => (Type::Bool, None),
|
||||||
|
|
||||||
(Type::Unknown, _) => (Type::Bool, None),
|
(Type::Unknown, _) => (Type::Bool, None),
|
||||||
(_, Type::Unknown) => (Type::Bool, None),
|
(_, Type::Unknown) => (Type::Bool, None),
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -9,3 +9,6 @@ edition = "2018"
|
|||||||
thiserror = "1.0.29"
|
thiserror = "1.0.29"
|
||||||
miette = "3.0.0"
|
miette = "3.0.0"
|
||||||
serde = {version = "1.0.130", features = ["derive"]}
|
serde = {version = "1.0.130", features = ["derive"]}
|
||||||
|
chrono = { version="0.4.19", features=["serde"] }
|
||||||
|
chrono-humanize = "0.2.1"
|
||||||
|
byte-unit = "4.0.9"
|
@ -1,5 +1,5 @@
|
|||||||
use super::{Call, CellPath, Expression, FullCellPath, Operator, RangeOperator};
|
use super::{Call, CellPath, Expression, FullCellPath, Operator, RangeOperator};
|
||||||
use crate::{BlockId, Signature, Span, VarId};
|
use crate::{BlockId, Signature, Span, Spanned, Unit, VarId};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
@ -23,6 +23,9 @@ pub enum Expr {
|
|||||||
List(Vec<Expression>),
|
List(Vec<Expression>),
|
||||||
Table(Vec<Expression>, Vec<Vec<Expression>>),
|
Table(Vec<Expression>, Vec<Vec<Expression>>),
|
||||||
Keyword(Vec<u8>, Span, Box<Expression>),
|
Keyword(Vec<u8>, Span, Box<Expression>),
|
||||||
|
ValueWithUnit(Box<Expression>, Spanned<Unit>),
|
||||||
|
Filepath(String),
|
||||||
|
GlobPattern(String),
|
||||||
String(String), // FIXME: improve this in the future?
|
String(String), // FIXME: improve this in the future?
|
||||||
CellPath(CellPath),
|
CellPath(CellPath),
|
||||||
FullCellPath(Box<FullCellPath>),
|
FullCellPath(Box<FullCellPath>),
|
||||||
|
@ -12,12 +12,12 @@ pub struct EngineState {
|
|||||||
vars: Vec<Type>,
|
vars: Vec<Type>,
|
||||||
decls: Vec<Box<dyn Command>>,
|
decls: Vec<Box<dyn Command>>,
|
||||||
blocks: Vec<Block>,
|
blocks: Vec<Block>,
|
||||||
scope: Vec<ScopeFrame>,
|
pub scope: Vec<ScopeFrame>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ScopeFrame {
|
pub struct ScopeFrame {
|
||||||
vars: HashMap<Vec<u8>, VarId>,
|
pub vars: HashMap<Vec<u8>, VarId>,
|
||||||
decls: HashMap<Vec<u8>, DeclId>,
|
decls: HashMap<Vec<u8>, DeclId>,
|
||||||
aliases: HashMap<Vec<u8>, Vec<Span>>,
|
aliases: HashMap<Vec<u8>, Vec<Span>>,
|
||||||
modules: HashMap<Vec<u8>, BlockId>,
|
modules: HashMap<Vec<u8>, BlockId>,
|
||||||
@ -375,7 +375,7 @@ impl<'a> StateWorkingSet<'a> {
|
|||||||
|
|
||||||
if let Some(decl_id) = scope.decls.get(name) {
|
if let Some(decl_id) = scope.decls.get(name) {
|
||||||
if !hiding.contains(decl_id) {
|
if !hiding.contains(decl_id) {
|
||||||
// Do not hide already hidden decl
|
// Hide decl only if it's not already hidden
|
||||||
last_scope_frame.hiding.insert(*decl_id);
|
last_scope_frame.hiding.insert(*decl_id);
|
||||||
return Some(*decl_id);
|
return Some(*decl_id);
|
||||||
}
|
}
|
||||||
@ -409,8 +409,6 @@ impl<'a> StateWorkingSet<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate_overlay(&mut self, overlay: Vec<(Vec<u8>, DeclId)>) {
|
pub fn activate_overlay(&mut self, overlay: Vec<(Vec<u8>, DeclId)>) {
|
||||||
// TODO: This will overwrite all existing definitions in a scope. When we add deactivate,
|
|
||||||
// we need to re-think how make it recoverable.
|
|
||||||
let scope_frame = self
|
let scope_frame = self
|
||||||
.delta
|
.delta
|
||||||
.scope
|
.scope
|
||||||
|
@ -78,4 +78,53 @@ pub enum ShellError {
|
|||||||
#[error("Flag not found")]
|
#[error("Flag not found")]
|
||||||
#[diagnostic(code(nu::shell::flag_not_found), url(docsrs))]
|
#[diagnostic(code(nu::shell::flag_not_found), url(docsrs))]
|
||||||
FlagNotFound(String, #[label("{0} not found")] Span),
|
FlagNotFound(String, #[label("{0} not found")] Span),
|
||||||
|
|
||||||
|
#[error("File not found")]
|
||||||
|
#[diagnostic(code(nu::shell::file_not_found), url(docsrs))]
|
||||||
|
FileNotFound(#[label("file not found")] Span),
|
||||||
|
|
||||||
|
#[error("File not found")]
|
||||||
|
#[diagnostic(code(nu::shell::file_not_found), url(docsrs))]
|
||||||
|
FileNotFoundCustom(String, #[label("{0}")] Span),
|
||||||
|
|
||||||
|
#[error("Directory not found")]
|
||||||
|
#[diagnostic(code(nu::shell::directory_not_found), url(docsrs))]
|
||||||
|
DirectoryNotFound(#[label("directory not found")] Span),
|
||||||
|
|
||||||
|
#[error("File not found")]
|
||||||
|
#[diagnostic(code(nu::shell::file_not_found), url(docsrs))]
|
||||||
|
DirectoryNotFoundCustom(String, #[label("{0}")] Span),
|
||||||
|
|
||||||
|
#[error("Move not possible")]
|
||||||
|
#[diagnostic(code(nu::shell::move_not_possible), url(docsrs))]
|
||||||
|
MoveNotPossible {
|
||||||
|
source_message: String,
|
||||||
|
#[label("{source_message}")]
|
||||||
|
source_span: Span,
|
||||||
|
destination_message: String,
|
||||||
|
#[label("{destination_message}")]
|
||||||
|
destination_span: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Move not possible")]
|
||||||
|
#[diagnostic(code(nu::shell::move_not_possible_single), url(docsrs))]
|
||||||
|
MoveNotPossibleSingle(String, #[label("{0}")] Span),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for ShellError {
|
||||||
|
fn from(input: std::io::Error) -> ShellError {
|
||||||
|
ShellError::InternalError(format!("{:?}", input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<Box<dyn std::error::Error>> for ShellError {
|
||||||
|
fn from(input: Box<dyn std::error::Error>) -> ShellError {
|
||||||
|
ShellError::InternalError(input.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
|
||||||
|
fn from(input: Box<dyn std::error::Error + Send + Sync>) -> ShellError {
|
||||||
|
ShellError::InternalError(format!("{:?}", input))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
use miette::SourceSpan;
|
use miette::SourceSpan;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub struct Spanned<T> {
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Spanned<T>
|
||||||
|
where
|
||||||
|
T: Clone + std::fmt::Debug,
|
||||||
|
{
|
||||||
pub item: T,
|
pub item: T,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ pub enum SyntaxShape {
|
|||||||
Int,
|
Int,
|
||||||
|
|
||||||
/// A filepath is allowed
|
/// A filepath is allowed
|
||||||
FilePath,
|
Filepath,
|
||||||
|
|
||||||
/// A glob pattern is allowed, eg `foo*`
|
/// A glob pattern is allowed, eg `foo*`
|
||||||
GlobPattern,
|
GlobPattern,
|
||||||
@ -86,7 +86,7 @@ impl SyntaxShape {
|
|||||||
SyntaxShape::Custom(custom, _) => custom.to_type(),
|
SyntaxShape::Custom(custom, _) => custom.to_type(),
|
||||||
SyntaxShape::Duration => Type::Duration,
|
SyntaxShape::Duration => Type::Duration,
|
||||||
SyntaxShape::Expression => Type::Unknown,
|
SyntaxShape::Expression => Type::Unknown,
|
||||||
SyntaxShape::FilePath => Type::String,
|
SyntaxShape::Filepath => Type::String,
|
||||||
SyntaxShape::Filesize => Type::Filesize,
|
SyntaxShape::Filesize => Type::Filesize,
|
||||||
SyntaxShape::FullCellPath => Type::Unknown,
|
SyntaxShape::FullCellPath => Type::Unknown,
|
||||||
SyntaxShape::GlobPattern => Type::String,
|
SyntaxShape::GlobPattern => Type::String,
|
||||||
|
@ -12,7 +12,7 @@ pub enum Type {
|
|||||||
Block,
|
Block,
|
||||||
CellPath,
|
CellPath,
|
||||||
Duration,
|
Duration,
|
||||||
FilePath,
|
Date,
|
||||||
Filesize,
|
Filesize,
|
||||||
List(Box<Type>),
|
List(Box<Type>),
|
||||||
Number,
|
Number,
|
||||||
@ -31,8 +31,8 @@ impl Display for Type {
|
|||||||
Type::Block => write!(f, "block"),
|
Type::Block => write!(f, "block"),
|
||||||
Type::Bool => write!(f, "bool"),
|
Type::Bool => write!(f, "bool"),
|
||||||
Type::CellPath => write!(f, "cell path"),
|
Type::CellPath => write!(f, "cell path"),
|
||||||
|
Type::Date => write!(f, "date"),
|
||||||
Type::Duration => write!(f, "duration"),
|
Type::Duration => write!(f, "duration"),
|
||||||
Type::FilePath => write!(f, "filepath"),
|
|
||||||
Type::Filesize => write!(f, "filesize"),
|
Type::Filesize => write!(f, "filesize"),
|
||||||
Type::Float => write!(f, "float"),
|
Type::Float => write!(f, "float"),
|
||||||
Type::Int => write!(f, "int"),
|
Type::Int => write!(f, "int"),
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
mod range;
|
mod range;
|
||||||
mod row;
|
mod row;
|
||||||
mod stream;
|
mod stream;
|
||||||
|
mod unit;
|
||||||
|
|
||||||
|
use chrono::{DateTime, FixedOffset};
|
||||||
|
use chrono_humanize::HumanTime;
|
||||||
pub use range::*;
|
pub use range::*;
|
||||||
pub use row::*;
|
pub use row::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
pub use stream::*;
|
pub use stream::*;
|
||||||
|
pub use unit::*;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
@ -26,11 +30,15 @@ pub enum Value {
|
|||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
Filesize {
|
Filesize {
|
||||||
val: u64,
|
val: i64,
|
||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
Duration {
|
Duration {
|
||||||
val: u64,
|
val: i64,
|
||||||
|
span: Span,
|
||||||
|
},
|
||||||
|
Date {
|
||||||
|
val: DateTime<FixedOffset>,
|
||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
Range {
|
Range {
|
||||||
@ -95,6 +103,7 @@ impl Value {
|
|||||||
Value::Float { span, .. } => *span,
|
Value::Float { span, .. } => *span,
|
||||||
Value::Filesize { span, .. } => *span,
|
Value::Filesize { span, .. } => *span,
|
||||||
Value::Duration { span, .. } => *span,
|
Value::Duration { span, .. } => *span,
|
||||||
|
Value::Date { span, .. } => *span,
|
||||||
Value::Range { span, .. } => *span,
|
Value::Range { span, .. } => *span,
|
||||||
Value::String { span, .. } => *span,
|
Value::String { span, .. } => *span,
|
||||||
Value::Record { span, .. } => *span,
|
Value::Record { span, .. } => *span,
|
||||||
@ -115,6 +124,7 @@ impl Value {
|
|||||||
Value::Float { span, .. } => *span = new_span,
|
Value::Float { span, .. } => *span = new_span,
|
||||||
Value::Filesize { span, .. } => *span = new_span,
|
Value::Filesize { span, .. } => *span = new_span,
|
||||||
Value::Duration { span, .. } => *span = new_span,
|
Value::Duration { span, .. } => *span = new_span,
|
||||||
|
Value::Date { span, .. } => *span = new_span,
|
||||||
Value::Range { span, .. } => *span = new_span,
|
Value::Range { span, .. } => *span = new_span,
|
||||||
Value::String { span, .. } => *span = new_span,
|
Value::String { span, .. } => *span = new_span,
|
||||||
Value::Record { span, .. } => *span = new_span,
|
Value::Record { span, .. } => *span = new_span,
|
||||||
@ -138,6 +148,7 @@ impl Value {
|
|||||||
Value::Float { .. } => Type::Float,
|
Value::Float { .. } => Type::Float,
|
||||||
Value::Filesize { .. } => Type::Filesize,
|
Value::Filesize { .. } => Type::Filesize,
|
||||||
Value::Duration { .. } => Type::Duration,
|
Value::Duration { .. } => Type::Duration,
|
||||||
|
Value::Date { .. } => Type::Date,
|
||||||
Value::Range { .. } => Type::Range,
|
Value::Range { .. } => Type::Range,
|
||||||
Value::String { .. } => Type::String,
|
Value::String { .. } => Type::String,
|
||||||
Value::Record { cols, vals, .. } => {
|
Value::Record { cols, vals, .. } => {
|
||||||
@ -159,8 +170,9 @@ impl Value {
|
|||||||
Value::Bool { val, .. } => val.to_string(),
|
Value::Bool { val, .. } => val.to_string(),
|
||||||
Value::Int { val, .. } => val.to_string(),
|
Value::Int { val, .. } => val.to_string(),
|
||||||
Value::Float { val, .. } => val.to_string(),
|
Value::Float { val, .. } => val.to_string(),
|
||||||
Value::Filesize { val, .. } => format!("{} bytes", val),
|
Value::Filesize { val, .. } => format_filesize(val),
|
||||||
Value::Duration { val, .. } => format!("{} ns", val),
|
Value::Duration { val, .. } => format_duration(val),
|
||||||
|
Value::Date { val, .. } => HumanTime::from(val).to_string(),
|
||||||
Value::Range { val, .. } => {
|
Value::Range { val, .. } => {
|
||||||
format!(
|
format!(
|
||||||
"range: [{}]",
|
"range: [{}]",
|
||||||
@ -202,6 +214,7 @@ impl Value {
|
|||||||
Value::Float { val, .. } => val.to_string(),
|
Value::Float { val, .. } => val.to_string(),
|
||||||
Value::Filesize { val, .. } => format!("{} bytes", val),
|
Value::Filesize { val, .. } => format!("{} bytes", val),
|
||||||
Value::Duration { val, .. } => format!("{} ns", val),
|
Value::Duration { val, .. } => format!("{} ns", val),
|
||||||
|
Value::Date { val, .. } => format!("{:?}", val),
|
||||||
Value::Range { val, .. } => val
|
Value::Range { val, .. } => val
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|x| x.into_string())
|
.map(|x| x.into_string())
|
||||||
@ -391,6 +404,18 @@ impl Value {
|
|||||||
val: lhs.to_string() + rhs,
|
val: lhs.to_string() + rhs,
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
|
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Duration {
|
||||||
|
val: *lhs + *rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Filesize {
|
||||||
|
val: *lhs + *rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
op_span: op,
|
op_span: op,
|
||||||
@ -421,6 +446,18 @@ impl Value {
|
|||||||
val: lhs - rhs,
|
val: lhs - rhs,
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
|
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Duration {
|
||||||
|
val: *lhs - *rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Filesize {
|
||||||
|
val: *lhs - *rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
op_span: op,
|
op_span: op,
|
||||||
@ -542,6 +579,19 @@ impl Value {
|
|||||||
val: lhs < rhs,
|
val: lhs < rhs,
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
|
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Bool {
|
||||||
|
val: lhs < rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Bool {
|
||||||
|
val: lhs < rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
op_span: op,
|
op_span: op,
|
||||||
lhs_ty: self.get_type(),
|
lhs_ty: self.get_type(),
|
||||||
@ -571,6 +621,18 @@ impl Value {
|
|||||||
val: lhs <= rhs,
|
val: lhs <= rhs,
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
|
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Bool {
|
||||||
|
val: lhs <= rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Bool {
|
||||||
|
val: lhs <= rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
op_span: op,
|
op_span: op,
|
||||||
lhs_ty: self.get_type(),
|
lhs_ty: self.get_type(),
|
||||||
@ -600,6 +662,18 @@ impl Value {
|
|||||||
val: lhs > rhs,
|
val: lhs > rhs,
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
|
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Bool {
|
||||||
|
val: lhs > rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Bool {
|
||||||
|
val: lhs > rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
op_span: op,
|
op_span: op,
|
||||||
lhs_ty: self.get_type(),
|
lhs_ty: self.get_type(),
|
||||||
@ -629,6 +703,18 @@ impl Value {
|
|||||||
val: lhs >= rhs,
|
val: lhs >= rhs,
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
|
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Bool {
|
||||||
|
val: lhs >= rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Bool {
|
||||||
|
val: lhs >= rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
op_span: op,
|
op_span: op,
|
||||||
lhs_ty: self.get_type(),
|
lhs_ty: self.get_type(),
|
||||||
@ -665,6 +751,18 @@ impl Value {
|
|||||||
val: lhs == rhs,
|
val: lhs == rhs,
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
|
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Bool {
|
||||||
|
val: lhs == rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Bool {
|
||||||
|
val: lhs == rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
(Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => Ok(Value::Bool {
|
(Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => Ok(Value::Bool {
|
||||||
val: lhs == rhs,
|
val: lhs == rhs,
|
||||||
span,
|
span,
|
||||||
@ -720,6 +818,18 @@ impl Value {
|
|||||||
val: lhs != rhs,
|
val: lhs != rhs,
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
|
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Bool {
|
||||||
|
val: lhs != rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
|
||||||
|
Ok(Value::Bool {
|
||||||
|
val: lhs != rhs,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
(Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => Ok(Value::Bool {
|
(Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => Ok(Value::Bool {
|
||||||
val: lhs != rhs,
|
val: lhs != rhs,
|
||||||
span,
|
span,
|
||||||
@ -750,3 +860,69 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Format a duration in nanoseconds into a string
|
||||||
|
pub fn format_duration(duration: i64) -> String {
|
||||||
|
let (sign, duration) = if duration >= 0 {
|
||||||
|
(1, duration)
|
||||||
|
} else {
|
||||||
|
(-1, -duration)
|
||||||
|
};
|
||||||
|
let (micros, nanos): (i64, i64) = (duration / 1000, duration % 1000);
|
||||||
|
let (millis, micros): (i64, i64) = (micros / 1000, micros % 1000);
|
||||||
|
let (secs, millis): (i64, i64) = (millis / 1000, millis % 1000);
|
||||||
|
let (mins, secs): (i64, i64) = (secs / 60, secs % 60);
|
||||||
|
let (hours, mins): (i64, i64) = (mins / 60, mins % 60);
|
||||||
|
let (days, hours): (i64, i64) = (hours / 24, hours % 24);
|
||||||
|
|
||||||
|
let mut output_prep = vec![];
|
||||||
|
|
||||||
|
if days != 0 {
|
||||||
|
output_prep.push(format!("{}day", days));
|
||||||
|
}
|
||||||
|
|
||||||
|
if hours != 0 {
|
||||||
|
output_prep.push(format!("{}hr", hours));
|
||||||
|
}
|
||||||
|
|
||||||
|
if mins != 0 {
|
||||||
|
output_prep.push(format!("{}min", mins));
|
||||||
|
}
|
||||||
|
// output 0sec for zero duration
|
||||||
|
if duration == 0 || secs != 0 {
|
||||||
|
output_prep.push(format!("{}sec", secs));
|
||||||
|
}
|
||||||
|
|
||||||
|
if millis != 0 {
|
||||||
|
output_prep.push(format!("{}ms", millis));
|
||||||
|
}
|
||||||
|
|
||||||
|
if micros != 0 {
|
||||||
|
output_prep.push(format!("{}us", micros));
|
||||||
|
}
|
||||||
|
|
||||||
|
if nanos != 0 {
|
||||||
|
output_prep.push(format!("{}ns", nanos));
|
||||||
|
}
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"{}{}",
|
||||||
|
if sign == -1 { "-" } else { "" },
|
||||||
|
output_prep.join(" ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_filesize(num_bytes: i64) -> String {
|
||||||
|
let byte = byte_unit::Byte::from_bytes(num_bytes as u128);
|
||||||
|
|
||||||
|
if byte.get_bytes() == 0u128 {
|
||||||
|
return "—".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
let byte = byte.get_appropriate_unit(false);
|
||||||
|
|
||||||
|
match byte.get_unit() {
|
||||||
|
byte_unit::ByteUnit::B => format!("{} B ", byte.get_value()),
|
||||||
|
_ => byte.format(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
27
crates/nu-protocol/src/value/unit.rs
Normal file
27
crates/nu-protocol/src/value/unit.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum Unit {
|
||||||
|
// Filesize units: metric
|
||||||
|
Byte,
|
||||||
|
Kilobyte,
|
||||||
|
Megabyte,
|
||||||
|
Gigabyte,
|
||||||
|
Terabyte,
|
||||||
|
Petabyte,
|
||||||
|
|
||||||
|
// Filesize units: ISO/IEC 80000
|
||||||
|
Kibibyte,
|
||||||
|
Mebibyte,
|
||||||
|
Gibibyte,
|
||||||
|
Tebibyte,
|
||||||
|
Pebibyte,
|
||||||
|
|
||||||
|
// Duration units
|
||||||
|
Nanosecond,
|
||||||
|
Microsecond,
|
||||||
|
Millisecond,
|
||||||
|
Second,
|
||||||
|
Minute,
|
||||||
|
Hour,
|
||||||
|
Day,
|
||||||
|
Week,
|
||||||
|
}
|
@ -66,7 +66,7 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
use reedline::{DefaultCompletionActionHandler, FileBackedHistory, Reedline, Signal};
|
use reedline::{FileBackedHistory, ListCompletionHandler, Reedline, Signal};
|
||||||
|
|
||||||
let completer = NuCompleter::new(engine_state.clone());
|
let completer = NuCompleter::new(engine_state.clone());
|
||||||
let mut entry_num = 0;
|
let mut entry_num = 0;
|
||||||
@ -81,7 +81,7 @@ fn main() -> Result<()> {
|
|||||||
engine_state: engine_state.clone(),
|
engine_state: engine_state.clone(),
|
||||||
}))
|
}))
|
||||||
.with_completion_action_handler(Box::new(
|
.with_completion_action_handler(Box::new(
|
||||||
DefaultCompletionActionHandler::default().with_completer(Box::new(completer)),
|
ListCompletionHandler::default().with_completer(Box::new(completer)),
|
||||||
))
|
))
|
||||||
.with_validator(Box::new(NuValidator {
|
.with_validator(Box::new(NuValidator {
|
||||||
engine_state: engine_state.clone(),
|
engine_state: engine_state.clone(),
|
||||||
|
49
src/tests.rs
49
src/tests.rs
@ -441,6 +441,46 @@ fn hide_twice_not_allowed() -> TestResult {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hides_import_1() -> TestResult {
|
||||||
|
fail_test(
|
||||||
|
r#"module spam { export def foo [] { "foo" } }; use spam; hide spam.foo; foo"#,
|
||||||
|
"not found",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hides_import_2() -> TestResult {
|
||||||
|
fail_test(
|
||||||
|
r#"module spam { export def foo [] { "foo" } }; use spam; hide spam.*; foo"#,
|
||||||
|
"not found",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hides_import_3() -> TestResult {
|
||||||
|
fail_test(
|
||||||
|
r#"module spam { export def foo [] { "foo" } }; use spam; hide spam.[foo]; foo"#,
|
||||||
|
"not found",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hides_import_4() -> TestResult {
|
||||||
|
fail_test(
|
||||||
|
r#"module spam { export def foo [] { "foo" } }; use spam.foo; hide foo; foo"#,
|
||||||
|
"not found",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hides_import_5() -> TestResult {
|
||||||
|
fail_test(
|
||||||
|
r#"module spam { export def foo [] { "foo" } }; use spam.*; hide foo; foo"#,
|
||||||
|
"not found",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn def_twice_should_fail() -> TestResult {
|
fn def_twice_should_fail() -> TestResult {
|
||||||
fail_test(
|
fail_test(
|
||||||
@ -449,6 +489,15 @@ fn def_twice_should_fail() -> TestResult {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: This test fails if executed each command on a separate line in REPL
|
||||||
|
#[test]
|
||||||
|
fn use_import_after_hide() -> TestResult {
|
||||||
|
run_test(
|
||||||
|
r#"module spam { export def foo [] { "foo" } }; use spam.foo; hide foo; use spam.foo; foo"#,
|
||||||
|
"foo",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_json_1() -> TestResult {
|
fn from_json_1() -> TestResult {
|
||||||
run_test(r#"('{"name": "Fred"}' | from json).name"#, "Fred")
|
run_test(r#"('{"name": "Fred"}' | from json).name"#, "Fred")
|
||||||
|
Loading…
Reference in New Issue
Block a user