Skip to main content

nu_command/path/
self_.rs

1use nu_engine::command_prelude::*;
2use nu_path::expand_path_with;
3use nu_protocol::{
4    engine::StateWorkingSet,
5    shell_error::{self, generic::GenericError, io::IoError},
6};
7
8#[derive(Clone)]
9pub struct PathSelf;
10
11impl Command for PathSelf {
12    fn name(&self) -> &str {
13        "path self"
14    }
15
16    fn signature(&self) -> Signature {
17        Signature::build("path self")
18            .input_output_type(Type::Nothing, Type::String)
19            .allow_variants_without_examples(true)
20            .optional(
21                "path",
22                SyntaxShape::Filepath,
23                "Path to get instead of the current file.",
24            )
25            .category(Category::Path)
26    }
27
28    fn description(&self) -> &str {
29        "Get the absolute path of the script or module containing this command at parse time."
30    }
31
32    fn is_const(&self) -> bool {
33        true
34    }
35
36    fn run(
37        &self,
38        _engine_state: &EngineState,
39        _stack: &mut Stack,
40        call: &Call,
41        _input: PipelineData,
42    ) -> Result<PipelineData, ShellError> {
43        Err(ShellError::Generic(
44            GenericError::new(
45                "this command can only run during parse-time",
46                "can't run after parse-time",
47                call.head,
48            )
49            .with_help("try assigning this command's output to a const variable"),
50        ))
51    }
52
53    fn run_const(
54        &self,
55        working_set: &StateWorkingSet,
56        call: &Call,
57        _input: PipelineData,
58    ) -> Result<PipelineData, ShellError> {
59        let path: Option<String> = call.opt_const(working_set, 0)?;
60        let cwd = working_set.permanent_state.cwd(None)?;
61        let current_file = working_set.files.top().ok_or_else(|| {
62            IoError::new_with_additional_context(
63                shell_error::io::ErrorKind::FileNotFound,
64                call.head,
65                None,
66                "Couldn't find current file",
67            )
68        })?;
69
70        let out = if let Some(path) = path {
71            let dir = expand_path_with(
72                current_file.parent().ok_or_else(|| {
73                    IoError::new_with_additional_context(
74                        shell_error::io::ErrorKind::FileNotFound,
75                        call.head,
76                        current_file.to_owned(),
77                        "Couldn't find current file's parent.",
78                    )
79                })?,
80                &cwd,
81                true,
82            );
83            Value::string(
84                expand_path_with(path, dir, false).to_string_lossy(),
85                call.head,
86            )
87        } else {
88            Value::string(
89                expand_path_with(current_file, &cwd, true).to_string_lossy(),
90                call.head,
91            )
92        };
93
94        Ok(out.into_pipeline_data())
95    }
96
97    fn examples(&self) -> Vec<Example<'_>> {
98        vec![
99            Example {
100                description: "Get the path of the current file.",
101                example: "const current_file = path self",
102                result: None,
103            },
104            Example {
105                description: "Get the path of the directory containing the current file.",
106                example: "const current_file = path self .",
107                result: None,
108            },
109            #[cfg(windows)]
110            Example {
111                description: "Get the absolute form of a path relative to the current file.",
112                example: r#"const current_file = path self ..\foo"#,
113                result: None,
114            },
115            #[cfg(not(windows))]
116            Example {
117                description: "Get the absolute form of a path relative to the current file.",
118                example: "const current_file = path self ../foo",
119                result: None,
120            },
121        ]
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    #[ignore = "`path self` fails at parse time already"]
131    fn test_examples() -> nu_test_support::Result {
132        nu_test_support::test().examples(PathSelf)
133    }
134}