Skip to main content

nu_cmd_base/
util.rs

1use nu_protocol::{
2    Range, ShellError, Span, Value,
3    engine::{EngineState, Stack},
4    shell_error::generic::GenericError,
5};
6use std::ops::Bound;
7
8type MakeRangeError = fn(&str, Span) -> ShellError;
9
10/// Returns a inclusive pair of boundary in given `range`.
11pub fn process_range(range: &Range) -> Result<(isize, isize), MakeRangeError> {
12    match range {
13        Range::IntRange(range) => {
14            let start = range.start().try_into().unwrap_or(0);
15            let end = match range.end() {
16                Bound::Included(v) => v as isize,
17                Bound::Excluded(v) => (v - 1) as isize,
18                Bound::Unbounded => isize::MAX,
19            };
20            Ok((start, end))
21        }
22        Range::FloatRange(_) => Err(|msg, span| ShellError::TypeMismatch {
23            err_message: msg.to_string(),
24            span,
25        }),
26    }
27}
28
29const HELP_MSG: &str = "Nushell's config file can be found with the command: $nu.config-path. \
30For more help: (https://nushell.sh/book/configuration.html#configurations-with-built-in-commands)";
31
32fn get_editor_commandline(
33    value: &Value,
34    var_name: &str,
35) -> Result<(String, Vec<String>), ShellError> {
36    match value {
37        Value::String { val, .. } if !val.is_empty() => Ok((val.to_string(), Vec::new())),
38        Value::List { vals, .. } if !vals.is_empty() => {
39            let mut editor_cmd = vals.iter().map(|l| l.coerce_string());
40            match editor_cmd.next().transpose()? {
41                Some(editor) if !editor.is_empty() => {
42                    let params = editor_cmd.collect::<Result<_, ShellError>>()?;
43                    Ok((editor, params))
44                }
45                _ => Err(ShellError::Generic(
46                    GenericError::new(
47                        "Editor executable is missing",
48                        "Set the first element to an executable",
49                        value.span(),
50                    )
51                    .with_help(HELP_MSG),
52                )),
53            }
54        }
55        Value::String { .. } | Value::List { .. } => Err(ShellError::Generic(
56            GenericError::new(
57                format!("{var_name} should be a non-empty string or list<String>"),
58                "Specify an executable here",
59                value.span(),
60            )
61            .with_help(HELP_MSG),
62        )),
63        x => Err(ShellError::CantConvert {
64            to_type: "string or list<string>".into(),
65            from_type: x.get_type().to_string(),
66            span: value.span(),
67            help: None,
68        }),
69    }
70}
71
72pub fn get_editor(
73    engine_state: &EngineState,
74    stack: &Stack,
75    span: Span,
76) -> Result<(String, Vec<String>), ShellError> {
77    let config = stack.get_config(engine_state);
78
79    if let Ok(buff_editor) =
80        get_editor_commandline(&config.buffer_editor, "$env.config.buffer_editor")
81    {
82        Ok(buff_editor)
83    } else if let Some(value) = stack.get_env_var(engine_state, "VISUAL") {
84        get_editor_commandline(value, "$env.VISUAL")
85    } else if let Some(value) = stack.get_env_var(engine_state, "EDITOR") {
86        get_editor_commandline(value, "$env.EDITOR")
87    } else {
88        Err(ShellError::Generic(
89            GenericError::new(
90                "No editor configured",
91                "Please specify one via `$env.config.buffer_editor` or `$env.EDITOR`/`$env.VISUAL`",
92                span,
93            )
94            .with_help(HELP_MSG),
95        ))
96    }
97}