diff --git a/crates/nu-command/src/filesystem/open.rs b/crates/nu-command/src/filesystem/open.rs index 56ef50613b..f0c6466d8b 100644 --- a/crates/nu-command/src/filesystem/open.rs +++ b/crates/nu-command/src/filesystem/open.rs @@ -1,8 +1,9 @@ -use nu_engine::CallExt; +use nu_engine::{get_full_help, CallExt}; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - ByteStream, Category, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value, + ByteStream, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned, + SyntaxShape, Value, }; use std::io::{BufRead, BufReader, Read}; @@ -24,7 +25,7 @@ impl Command for Open { fn signature(&self) -> nu_protocol::Signature { Signature::build("open") - .required("filename", SyntaxShape::Filepath, "the filename to use") + .optional("filename", SyntaxShape::Filepath, "the filename to use") .switch("raw", "open file as raw binary", Some('r')) .category(Category::FileSystem) } @@ -34,14 +35,47 @@ impl Command for Open { engine_state: &EngineState, stack: &mut Stack, call: &Call, - _input: PipelineData, + input: PipelineData, ) -> Result { let raw = call.has_flag("raw"); let call_span = call.head; let ctrlc = engine_state.ctrlc.clone(); - let path = call.req::>(engine_state, stack, 0)?; + let path = call.opt::>(engine_state, stack, 0)?; + + let path = if let Some(path) = path { + path + } else { + // Collect a filename from the input + match input { + PipelineData::Value(Value::Nothing { .. }, ..) => { + return Ok(Value::String { + val: get_full_help( + &Open.signature(), + &Open.examples(), + engine_state, + stack, + ), + span: call.head, + } + .into_pipeline_data()) + } + PipelineData::Value(val, ..) => val.as_spanned_string()?, + _ => { + return Ok(Value::String { + val: get_full_help( + &Open.signature(), + &Open.examples(), + engine_state, + stack, + ), + span: call.head, + } + .into_pipeline_data()) + } + } + }; let arg_span = path.span; let path = Path::new(&path.item); @@ -117,6 +151,26 @@ impl Command for Open { } } } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Open a file, with structure (based on file extension)", + example: "open myfile.json", + result: None, + }, + Example { + description: "Open a file, as raw bytes", + example: "open myfile.json --raw", + result: None, + }, + Example { + description: "Open a file, using the input to get filename", + example: "echo 'myfile.txt' | open", + result: None, + }, + ] + } } fn permission_denied(dir: impl AsRef) -> bool { diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index c9d020ee7d..7d6486a961 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -190,6 +190,33 @@ impl Value { } } + pub fn as_spanned_string(&self) -> Result, ShellError> { + match self { + Value::String { val, span } => Ok(Spanned { + item: val.to_string(), + span: *span, + }), + Value::Binary { val, span } => Ok(match std::str::from_utf8(val) { + Ok(s) => Spanned { + item: s.to_string(), + span: *span, + }, + Err(_) => { + return Err(ShellError::CantConvert( + "binary".into(), + "string".into(), + self.span()?, + )) + } + }), + x => Err(ShellError::CantConvert( + "string".into(), + x.get_type().to_string(), + self.span()?, + )), + } + } + pub fn as_path(&self) -> Result { match self { Value::String { val, .. } => Ok(PathBuf::from(val)),