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}