1use nu_engine::command_prelude::*;
2use nu_path::expand_path_with;
3use nu_protocol::{
4 engine::StateWorkingSet,
5 shell_error::{self, 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::GenericError {
44 error: "this command can only run during parse-time".into(),
45 msg: "can't run after parse-time".into(),
46 span: Some(call.head),
47 help: Some("try assigning this command's output to a const variable".into()),
48 inner: vec![],
49 })
50 }
51
52 fn run_const(
53 &self,
54 working_set: &StateWorkingSet,
55 call: &Call,
56 _input: PipelineData,
57 ) -> Result<PipelineData, ShellError> {
58 let path: Option<String> = call.opt_const(working_set, 0)?;
59 let cwd = working_set.permanent_state.cwd(None)?;
60 let current_file = working_set.files.top().ok_or_else(|| {
61 IoError::new_with_additional_context(
62 shell_error::io::ErrorKind::FileNotFound,
63 call.head,
64 None,
65 "Couldn't find current file",
66 )
67 })?;
68
69 let out = if let Some(path) = path {
70 let dir = expand_path_with(
71 current_file.parent().ok_or_else(|| {
72 IoError::new_with_additional_context(
73 shell_error::io::ErrorKind::FileNotFound,
74 call.head,
75 current_file.to_owned(),
76 "Couldn't find current file's parent.",
77 )
78 })?,
79 &cwd,
80 true,
81 );
82 Value::string(
83 expand_path_with(path, dir, false).to_string_lossy(),
84 call.head,
85 )
86 } else {
87 Value::string(
88 expand_path_with(current_file, &cwd, true).to_string_lossy(),
89 call.head,
90 )
91 };
92
93 Ok(out.into_pipeline_data())
94 }
95
96 fn examples(&self) -> Vec<Example<'_>> {
97 vec![
98 Example {
99 description: "Get the path of the current file",
100 example: r#"const current_file = path self"#,
101 result: None,
102 },
103 Example {
104 description: "Get the path of the directory containing the current file",
105 example: r#"const current_file = path self ."#,
106 result: None,
107 },
108 #[cfg(windows)]
109 Example {
110 description: "Get the absolute form of a path relative to the current file",
111 example: r#"const current_file = path self ..\foo"#,
112 result: None,
113 },
114 #[cfg(not(windows))]
115 Example {
116 description: "Get the absolute form of a path relative to the current file",
117 example: r#"const current_file = path self ../foo"#,
118 result: None,
119 },
120 ]
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn test_examples() {
130 use crate::test_examples;
131
132 test_examples(PathSelf {})
133 }
134}