nushell/crates/nu-cmd-base/src/util.rs
Ian Manske ae5fed41ed
Path migration part 3: $nu paths (#13368)
# Description
Part 3 of replacing `std::path` types with `nu_path` types added in
#13115. This PR targets the paths listed in `$nu`. That is, the home,
config, data, and cache directories.
2024-08-01 10:16:31 +02:00

117 lines
4 KiB
Rust

use nu_path::AbsolutePathBuf;
use nu_protocol::{
engine::{EngineState, Stack},
Range, ShellError, Span, Value,
};
use std::ops::Bound;
pub fn get_init_cwd() -> AbsolutePathBuf {
std::env::current_dir()
.ok()
.and_then(|path| AbsolutePathBuf::try_from(path).ok())
.or_else(|| {
std::env::var("PWD")
.ok()
.and_then(|path| AbsolutePathBuf::try_from(path).ok())
})
.or_else(nu_path::home_dir)
.expect("Failed to get current working directory")
}
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> AbsolutePathBuf {
engine_state
.cwd(Some(stack))
.ok()
.unwrap_or_else(get_init_cwd)
}
type MakeRangeError = fn(&str, Span) -> ShellError;
/// Returns a inclusive pair of boundary in given `range`.
pub fn process_range(range: &Range) -> Result<(isize, isize), MakeRangeError> {
match range {
Range::IntRange(range) => {
let start = range.start().try_into().unwrap_or(0);
let end = match range.end() {
Bound::Included(v) => v as isize,
Bound::Excluded(v) => (v - 1) as isize,
Bound::Unbounded => isize::MAX,
};
Ok((start, end))
}
Range::FloatRange(_) => Err(|msg, span| ShellError::TypeMismatch {
err_message: msg.to_string(),
span,
}),
}
}
const HELP_MSG: &str = "Nushell's config file can be found with the command: $nu.config-path. \
For more help: (https://nushell.sh/book/configuration.html#configurations-with-built-in-commands)";
fn get_editor_commandline(
value: &Value,
var_name: &str,
) -> Result<(String, Vec<String>), ShellError> {
match value {
Value::String { val, .. } if !val.is_empty() => Ok((val.to_string(), Vec::new())),
Value::List { vals, .. } if !vals.is_empty() => {
let mut editor_cmd = vals.iter().map(|l| l.coerce_string());
match editor_cmd.next().transpose()? {
Some(editor) if !editor.is_empty() => {
let params = editor_cmd.collect::<Result<_, ShellError>>()?;
Ok((editor, params))
}
_ => Err(ShellError::GenericError {
error: "Editor executable is missing".into(),
msg: "Set the first element to an executable".into(),
span: Some(value.span()),
help: Some(HELP_MSG.into()),
inner: vec![],
}),
}
}
Value::String { .. } | Value::List { .. } => Err(ShellError::GenericError {
error: format!("{var_name} should be a non-empty string or list<String>"),
msg: "Specify an executable here".into(),
span: Some(value.span()),
help: Some(HELP_MSG.into()),
inner: vec![],
}),
x => Err(ShellError::CantConvert {
to_type: "string or list<string>".into(),
from_type: x.get_type().to_string(),
span: value.span(),
help: None,
}),
}
}
pub fn get_editor(
engine_state: &EngineState,
stack: &Stack,
span: Span,
) -> Result<(String, Vec<String>), ShellError> {
let config = engine_state.get_config();
let env_vars = stack.get_env_vars(engine_state);
if let Ok(buff_editor) =
get_editor_commandline(&config.buffer_editor, "$env.config.buffer_editor")
{
Ok(buff_editor)
} else if let Some(value) = env_vars.get("EDITOR") {
get_editor_commandline(value, "$env.EDITOR")
} else if let Some(value) = env_vars.get("VISUAL") {
get_editor_commandline(value, "$env.VISUAL")
} else {
Err(ShellError::GenericError {
error: "No editor configured".into(),
msg:
"Please specify one via `$env.config.buffer_editor` or `$env.EDITOR`/`$env.VISUAL`"
.into(),
span: Some(span),
help: Some(HELP_MSG.into()),
inner: vec![],
})
}
}