Add pathvar command (#3670)

* Add pathvar command

Signed-off-by: nathom <nathanthomas707@gmail.com>

* Add pathvar command to context

Signed-off-by: nathom <nathanthomas707@gmail.com>

* Add pathvar reset command

Signed-off-by: nathom <nathanthomas707@gmail.com>

* Update help message

Signed-off-by: nathom <nathanthomas707@gmail.com>

* Remove insert command

Signed-off-by: nathom <nathanthomas707@gmail.com>

* Remove unused import

Signed-off-by: nathom <nathanthomas707@gmail.com>

* Remove insert mod

Signed-off-by: nathom <nathanthomas707@gmail.com>

* Support for windows path separator

Signed-off-by: nathom <nathanthomas707@gmail.com>

* Clear clippy errors

Signed-off-by: nathom <nathanthomas707@gmail.com>

* Remove empty file

Signed-off-by: nathom <nathanthomas707@gmail.com>

* Formatting

Signed-off-by: nathom <nathanthomas707@gmail.com>
This commit is contained in:
Nathan Thomas 2021-06-24 20:58:37 -07:00 committed by GitHub
parent 6ba40773bb
commit a3f119e0bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 376 additions and 0 deletions

View File

@ -12,6 +12,7 @@ mod generators;
mod math;
mod network;
mod path;
mod pathvar;
mod platform;
mod random;
mod shells;
@ -43,6 +44,7 @@ pub use generators::*;
pub use math::*;
pub use network::*;
pub use path::*;
pub use pathvar::*;
pub use platform::*;
pub use random::*;
pub use shells::*;

View File

@ -0,0 +1,59 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged;
use nu_test_support::{NATIVE_PATH_ENV_SEPARATOR, NATIVE_PATH_ENV_VAR};
use std::path::PathBuf;
pub struct SubCommand;
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"pathvar add"
}
fn signature(&self) -> Signature {
Signature::build("pathvar add").required("path", SyntaxShape::FilePath, "path to add")
}
fn usage(&self) -> &str {
"Add a filepath to the start of the pathvar"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
add(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Add /usr/local/bin to the pathvar",
example: "pathvar add /usr/local/bin",
result: None,
}]
}
}
pub fn add(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ctx = &args.context;
let path_to_add: Tagged<PathBuf> = args.req(0)?;
let path = path_to_add.item.into_os_string().into_string();
if let Ok(mut path) = path {
path.push(NATIVE_PATH_ENV_SEPARATOR);
if let Some(old_pathvar) = ctx.scope.get_env(NATIVE_PATH_ENV_VAR) {
path.push_str(&old_pathvar);
ctx.scope.add_env_var(NATIVE_PATH_ENV_VAR, path);
Ok(OutputStream::empty())
} else {
Err(ShellError::unexpected("PATH not set"))
}
} else {
Err(ShellError::labeled_error(
"Invalid path.",
"cannot convert to string",
path_to_add.tag,
))
}
}

View File

@ -0,0 +1,58 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged;
use nu_test_support::{NATIVE_PATH_ENV_SEPARATOR, NATIVE_PATH_ENV_VAR};
use std::path::PathBuf;
pub struct SubCommand;
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"pathvar append"
}
fn signature(&self) -> Signature {
Signature::build("pathvar append").required("path", SyntaxShape::FilePath, "path to append")
}
fn usage(&self) -> &str {
"Add a path to the end of the pathvar"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
add(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Append /bin to the pathvar",
example: "pathvar append /bin",
result: None,
}]
}
}
pub fn add(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ctx = &args.context;
let path_to_append_arg: Tagged<PathBuf> = args.req(0)?;
let path_to_append = path_to_append_arg.item.into_os_string().into_string();
if let Ok(path) = path_to_append {
if let Some(mut pathvar) = ctx.scope.get_env(NATIVE_PATH_ENV_VAR) {
pathvar.push(NATIVE_PATH_ENV_SEPARATOR);
pathvar.push_str(&path);
ctx.scope.add_env_var(NATIVE_PATH_ENV_VAR, pathvar);
Ok(OutputStream::empty())
} else {
Err(ShellError::unexpected("PATH not set"))
}
} else {
Err(ShellError::labeled_error(
"Invalid path.",
"cannot convert to string",
path_to_append_arg.tag,
))
}
}

View File

@ -0,0 +1,58 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, Value};
use nu_test_support::{NATIVE_PATH_ENV_SEPARATOR, NATIVE_PATH_ENV_VAR};
pub struct Command;
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"pathvar"
}
fn signature(&self) -> Signature {
Signature::build("pathvar")
}
fn usage(&self) -> &str {
"Manipulate the PATH variable (or pathvar)."
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
get_pathvar(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Display the current session's pathvar",
example: "pathvar",
result: None,
},
Example {
description: "Add /usr/bin to the pathvar",
example: "pathvar add /usr/bin",
result: None,
},
Example {
description: "Remove the 3rd path in the pathvar",
example: "pathvar remove 2",
result: None,
},
]
}
}
pub fn get_pathvar(args: CommandArgs) -> Result<OutputStream, ShellError> {
if let Some(pathvar) = args.context.scope.get_env(NATIVE_PATH_ENV_VAR) {
let pathvar: Vec<Value> = pathvar
.split(NATIVE_PATH_ENV_SEPARATOR)
.map(Value::from)
.collect();
Ok(OutputStream::from(pathvar))
} else {
Err(ShellError::unexpected("PATH not set"))
}
}

View File

@ -0,0 +1,13 @@
pub mod add;
pub mod append;
pub mod command;
pub mod remove;
pub mod reset;
pub mod save;
pub use add::SubCommand as PathvarAdd;
pub use append::SubCommand as PathvarAppend;
pub use command::Command as Pathvar;
pub use remove::SubCommand as PathvarRemove;
pub use reset::SubCommand as PathvarReset;
pub use save::SubCommand as PathvarSave;

View File

@ -0,0 +1,66 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged;
use nu_test_support::{NATIVE_PATH_ENV_SEPARATOR, NATIVE_PATH_ENV_VAR};
pub struct SubCommand;
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"pathvar remove"
}
fn signature(&self) -> Signature {
Signature::build("pathvar remove").required(
"index",
SyntaxShape::Int,
"index of the path to remove (starting at 0)",
)
}
fn usage(&self) -> &str {
"Remove a path from the pathvar"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
remove(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Remove the second path from the pathvar",
example: "pathvar remove 1",
result: None,
}]
}
}
pub fn remove(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ctx = &args.context;
let index_to_remove_arg: Tagged<u64> = args.req(0)?;
let index_to_remove = index_to_remove_arg.item as usize;
if let Some(old_pathvar) = ctx.scope.get_env(NATIVE_PATH_ENV_VAR) {
let mut paths: Vec<&str> = old_pathvar.split(NATIVE_PATH_ENV_SEPARATOR).collect();
if index_to_remove >= paths.len() {
return Err(ShellError::labeled_error(
"Index out of bounds",
format!("the index must be between 0 and {}", paths.len() - 1),
index_to_remove_arg.tag,
));
}
paths.remove(index_to_remove);
ctx.scope.add_env_var(
NATIVE_PATH_ENV_VAR,
paths.join(&NATIVE_PATH_ENV_SEPARATOR.to_string()),
);
Ok(OutputStream::empty())
} else {
Err(ShellError::unexpected("PATH not set"))
}
}

View File

@ -0,0 +1,52 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, UntaggedValue};
use nu_test_support::{NATIVE_PATH_ENV_SEPARATOR, NATIVE_PATH_ENV_VAR};
pub struct SubCommand;
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"pathvar reset"
}
fn signature(&self) -> Signature {
Signature::build("pathvar reset")
}
fn usage(&self) -> &str {
"Reset the pathvar to the one specified in the config"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
reset(args)
}
}
pub fn reset(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let ctx = &args.context;
if let Some(global_cfg) = &mut ctx.configs().lock().global_config {
let default_pathvar = global_cfg.vars.get("path");
if let Some(pathvar) = default_pathvar {
if let UntaggedValue::Table(paths) = &pathvar.value {
let pathvar_str = paths
.iter()
.map(|x| x.as_string().expect("Error converting path to string"))
.join(&NATIVE_PATH_ENV_SEPARATOR.to_string());
ctx.scope.add_env_var(NATIVE_PATH_ENV_VAR, pathvar_str);
}
} else {
return Err(ShellError::untagged_runtime_error(
"Default path is not set in config file.",
));
}
Ok(OutputStream::empty())
} else {
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
.into_value(name);
Ok(OutputStream::one(value))
}
}

View File

@ -0,0 +1,57 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, UntaggedValue, Value};
use nu_test_support::{NATIVE_PATH_ENV_SEPARATOR, NATIVE_PATH_ENV_VAR};
pub struct SubCommand;
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"pathvar save"
}
fn signature(&self) -> Signature {
Signature::build("pathvar save")
}
fn usage(&self) -> &str {
"Save the current pathvar to the config file"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
save(args)
}
}
pub fn save(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let ctx = &args.context;
if let Some(global_cfg) = &mut ctx.configs().lock().global_config {
if let Some(pathvar) = ctx.scope.get_env(NATIVE_PATH_ENV_VAR) {
let paths: Vec<Value> = pathvar
.split(NATIVE_PATH_ENV_SEPARATOR)
.map(Value::from)
.collect();
let span_range = 0..paths.len();
let row = Value::new(
UntaggedValue::Table(paths),
Tag::from(Span::from(&span_range)),
);
global_cfg.vars.insert("path".to_string(), row);
global_cfg.write()?;
ctx.reload_config(global_cfg)?;
Ok(OutputStream::empty())
} else {
Err(ShellError::unexpected("PATH not set"))
}
} else {
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
.into_value(name);
Ok(OutputStream::one(value))
}
}

View File

@ -27,6 +27,12 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(Cd),
whole_stream_command(Remove),
whole_stream_command(Open),
whole_stream_command(Pathvar),
whole_stream_command(PathvarAdd),
whole_stream_command(PathvarRemove),
whole_stream_command(PathvarReset),
whole_stream_command(PathvarAppend),
whole_stream_command(PathvarSave),
whole_stream_command(Config),
whole_stream_command(ConfigGet),
whole_stream_command(ConfigSet),

View File

@ -14,6 +14,11 @@ pub const NATIVE_PATH_ENV_VAR: &str = "Path";
#[cfg(not(windows))]
pub const NATIVE_PATH_ENV_VAR: &str = "PATH";
#[cfg(windows)]
pub const NATIVE_PATH_ENV_SEPARATOR: char = ':';
#[cfg(not(windows))]
pub const NATIVE_PATH_ENV_SEPARATOR: char = ';';
impl Outcome {
pub fn new(out: String, err: String) -> Outcome {
Outcome { out, err }