add rm command + stubs for open and save

This commit is contained in:
xiuxiu62
2021-10-09 21:13:15 -07:00
parent 53f3d2572c
commit 97ca242634
9 changed files with 430 additions and 67 deletions

View File

@ -13,51 +13,25 @@ pub fn create_default_context() -> Rc<RefCell<EngineState>> {
let engine_state = engine_state.borrow();
let mut working_set = StateWorkingSet::new(&*engine_state);
working_set.add_decl(Box::new(Alias));
working_set.add_decl(Box::new(Benchmark));
working_set.add_decl(Box::new(BuildString));
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(Do));
working_set.add_decl(Box::new(Each));
working_set.add_decl(Box::new(ExportDef));
working_set.add_decl(Box::new(External));
working_set.add_decl(Box::new(For));
working_set.add_decl(Box::new(From));
working_set.add_decl(Box::new(FromJson));
working_set.add_decl(Box::new(Get));
working_set.add_decl(Box::new(Griddle));
working_set.add_decl(Box::new(Help));
working_set.add_decl(Box::new(Hide));
working_set.add_decl(Box::new(If));
working_set.add_decl(Box::new(Length));
working_set.add_decl(Box::new(Let));
working_set.add_decl(Box::new(LetEnv));
working_set.add_decl(Box::new(Lines));
working_set.add_decl(Box::new(Ls));
working_set.add_decl(Box::new(Mkdir));
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(Select));
working_set.add_decl(Box::new(Split));
working_set.add_decl(Box::new(SplitChars));
working_set.add_decl(Box::new(SplitColumn));
working_set.add_decl(Box::new(SplitRow));
working_set.add_decl(Box::new(Sys));
working_set.add_decl(Box::new(Table));
working_set.add_decl(Box::new(Touch));
working_set.add_decl(Box::new(Use));
working_set.add_decl(Box::new(Where));
working_set.add_decl(Box::new(Wrap));
macro_rules! bind_command {
( $command:expr ) => {
working_set.add_decl(Box::new($command));
};
( $( $command:expr ),* ) => {
$( working_set.add_decl(Box::new($command)); )*
};
}
// TODO: sort items categorically
#[rustfmt::skip] bind_command!( Alias, Benchmark, BuildString,
Cd, Cp, Def, Do, Each, ExportDef, External, For, From,
FromJson, Get, Griddle, Help, Hide, If, Length, Let, LetEnv,
Lines, Ls, Mkdir, Module, Mv, Open, Ps, Rm, Save, Select,
Split, SplitChars, SplitColumn, SplitRow, Sys, Table, Touch,
Use, Where, Wrap );
// This is a WIP proof of concept
working_set.add_decl(Box::new(ListGitBranches));
working_set.add_decl(Box::new(Git));
working_set.add_decl(Box::new(GitCheckout));
working_set.add_decl(Box::new(Source));
bind_command!(ListGitBranches, Git, GitCheckout, Source);
let sig = Signature::build("exit");
working_set.add_decl(sig.predeclare());

View File

@ -3,6 +3,9 @@ mod cp;
mod ls;
mod mkdir;
mod mv;
mod open;
mod rm;
mod save;
mod touch;
mod util;
@ -11,4 +14,7 @@ pub use cp::Cp;
pub use ls::Ls;
pub use mkdir::Mkdir;
pub use mv::Mv;
pub use open::Open;
pub use rm::Rm;
pub use save::Save;
pub use touch::Touch;

View File

@ -0,0 +1,53 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{ShellError, Signature, SyntaxShape, Value};
pub struct Open;
impl Command for Open {
fn name(&self) -> &str {
"open"
}
fn usage(&self) -> &str {
"Load a file into a cell, convert to table if possible (avoid by appending '--raw')."
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required(
"path",
SyntaxShape::Filepath,
"the file path to load values from",
)
.switch(
"raw",
"load content as a string instead of a table",
Some('r'),
)
.named(
"encoding",
SyntaxShape::String,
"encoding to use to open file",
Some('e'),
)
}
fn extra_usage(&self) -> &str {
r#"Multiple encodings are supported for reading text files by using
the '--encoding <encoding>' parameter. Here is an example of a few:
big5, euc-jp, euc-kr, gbk, iso-8859-1, utf-16, cp1252, latin5
For a more complete list of encodings please refer to the encoding_rs
documentation link at https://docs.rs/encoding_rs/0.8.28/encoding_rs/#statics"#
}
fn run(
&self,
_context: &EvaluationContext,
_call: &Call,
_input: Value,
) -> Result<Value, ShellError> {
unimplemented!();
}
}

View File

@ -0,0 +1,252 @@
use std::collections::HashMap;
use std::env::current_dir;
use std::os::unix::prelude::FileTypeExt;
use std::path::{Path, PathBuf};
use glob::Paths;
use nu_engine::CallExt;
use nu_protocol::ast::{Call, Expression};
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{ShellError, Signature, SyntaxShape, Value, ValueStream};
pub struct Rm;
// Where self.0 is the unexpanded target's positional index (i.e. call.positional[self.0].span)
struct Target(usize, PathBuf);
struct RmArgs {
targets: Vec<Target>,
recursive: bool,
trash: bool,
permanent: bool,
force: bool,
}
impl Command for Rm {
fn name(&self) -> &str {
"rm"
}
fn usage(&self) -> &str {
"Remove file(s)."
}
fn signature(&self) -> Signature {
Signature::build("rm")
.switch(
"trash",
"use the platform's recycle bin instead of permanently deleting",
Some('t'),
)
.switch(
"permanent",
"don't use recycle bin, delete permanently",
Some('p'),
)
.switch("recursive", "delete subdirectories recursively", Some('r'))
.switch("force", "suppress error when no file", Some('f'))
.rest(
"rest",
SyntaxShape::GlobPattern,
"the file path(s) to remove",
)
}
fn run(
&self,
context: &EvaluationContext,
call: &Call,
_input: Value,
) -> Result<Value, ShellError> {
rm(context, call)
}
}
fn rm(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
let trash = call.has_flag("trash");
let permanent = call.has_flag("permanent");
if trash && permanent {
return Err(ShellError::IncompatibleParametersSingle(
"Can't use \"--trash\" with \"--permanent\"".to_string(),
call.head,
));
// let trash_span = call.get_flag_expr("trash").unwrap().span;
// let perm_span = call.get_flag_expr("permanent").unwrap().span;
// let left_message = "cannot use".to_string();
// let right_message = "with".to_string();
// let (left_span, right_span) = match trash_span.start < perm_span.start {
// true => (trash_span, perm_span),
// false => (perm_span, trash_span),
// };
// return Err(ShellError::IncompatibleParameters {
// left_message,
// left_span,
// right_message,
// right_span,
// });
}
let current_path = current_dir()?;
let mut paths = call
.rest::<String>(context, 0)?
.into_iter()
.map(|path| current_path.join(path))
.peekable();
if paths.peek().is_none() {
return Err(ShellError::FileNotFound(call.positional[0].span));
}
// Expand and flatten files
let resolve_path = |i: usize, path: PathBuf| {
glob::glob(&path.to_string_lossy()).map_or_else(
|_| Vec::new(),
|path_iter| path_iter.flatten().map(|f| Target(i, f)).collect(),
)
};
let mut targets: Vec<Target> = vec![];
for (i, path) in paths.enumerate() {
let mut paths: Vec<Target> = resolve_path(i, path);
if paths.is_empty() {
return Err(ShellError::FileNotFound(call.positional[i].span));
}
targets.append(paths.as_mut());
}
let recursive = call.has_flag("recursive");
let force = call.has_flag("force");
let args = RmArgs {
targets,
recursive,
trash,
permanent,
force,
};
let response = rm_helper(call, args);
// let temp = rm_helper(call, args).flatten();
// let temp = input.flatten(call.head, move |_| rm_helper(call, args));
Ok(Value::Stream {
stream: ValueStream::from_stream(response.into_iter()),
span: call.head,
})
// Ok(Value::Nothing { span })
}
fn rm_helper(call: &Call, args: RmArgs) -> Vec<Value> {
let (targets, recursive, trash, permanent, force) = (
args.targets,
args.recursive,
args.trash,
args.permanent,
args.force,
);
#[cfg(not(feature = "trash-support"))]
{
if trash {
return vec![Value::Error {
error: ShellError::FeatureNotEnabled(call.get_flag_expr("trash").unwrap().span),
}];
}
}
if targets.is_empty() && !force {
return vec![Value::Error {
error: ShellError::FileNotFound(call.head),
}];
}
targets
.into_iter()
.map(move |target| {
let (i, f) = (target.0, target.1);
let is_empty = || match f.read_dir() {
Ok(mut p) => p.next().is_none(),
Err(_) => false,
};
if let Ok(metadata) = f.symlink_metadata() {
#[cfg(unix)]
let is_socket = metadata.file_type().is_socket();
#[cfg(unix)]
let is_fifo = metadata.file_type().is_fifo();
#[cfg(not(unix))]
let is_socket = false;
#[cfg(not(unix))]
let is_fifo = false;
if metadata.is_file()
|| metadata.file_type().is_symlink()
|| recursive
|| is_socket
|| is_fifo
|| is_empty()
{
let result;
#[cfg(feature = "trash-support")]
{
use std::io::Error;
result = if trash {
trash::delete(&f).map_err(|e: trash::Error| {
Error::new(ErrorKind::Other, format!("{:?}", e))
})
} else if metadata.is_file() {
std::fs::remove_file(&f)
} else {
std::fs::remove_dir_all(&f)
};
}
#[cfg(not(feature = "trash-support"))]
{
result = if metadata.is_file() || is_socket || is_fifo {
std::fs::remove_file(&f)
} else {
std::fs::remove_dir_all(&f)
};
}
if let Err(e) = result {
return Value::Error {
error: ShellError::RemoveNotPossible(
format!("Could not delete because: {:}\nTry '--trash' flag", e),
call.head,
),
};
} else {
return Value::String {
val: format!("deleted {:}", f.to_string_lossy()).into(),
span: call.positional[i].span,
};
}
} else {
return Value::Error {
error: ShellError::RemoveNotPossible(
"Cannot remove. try --recursive".to_string(),
call.positional[i].span,
),
};
}
} else {
return Value::Error {
error: ShellError::RemoveNotPossible(
"no such file or directory".to_string(),
call.positional[i].span,
),
};
}
})
.collect()
}

View File

@ -0,0 +1,39 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{ShellError, Signature, SyntaxShape, Value};
pub struct Save;
impl Command for Save {
fn name(&self) -> &str {
"save"
}
fn signature(&self) -> Signature {
Signature::build("save")
.optional(
"path",
SyntaxShape::Filepath,
"the path to save contents to",
)
.switch(
"raw",
"treat values as-is rather than auto-converting based on file extension",
Some('r'),
)
.switch("append", "append values rather than overriding", Some('a'))
}
fn usage(&self) -> &str {
"Save the contents of the pipeline to a file."
}
fn run(
&self,
_context: &EvaluationContext,
_call: &Call,
_input: Value,
) -> Result<Value, ShellError> {
unimplemented!();
}
}

View File

@ -64,7 +64,8 @@ impl Command for SubCommand {
fn split_chars(call: &Call, input: Value) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
let span = call.head;
Ok(input.flat_map(span, move |x| split_chars_helper(&x, span)))
let temp = input.flat_map(span, move |x| split_chars_helper(&x, span));
Ok(temp)
}
fn split_chars_helper(v: &Value, name: Span) -> Vec<Value> {