use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand}; use crate::errors::ShellError; use crate::object::Value; use crate::prelude::*; use std::path::{Path, PathBuf}; pub struct Save; #[derive(Deserialize)] pub struct SaveArgs { path: Option>, raw: bool, } impl WholeStreamCommand for Save { fn name(&self) -> &str { "save" } fn signature(&self) -> Signature { Signature::build("save") .optional("path", SyntaxType::Path) .switch("raw") } fn usage(&self) -> &str { "Save the contents of the pipeline to a file." } fn run( &self, args: CommandArgs, registry: &CommandRegistry, ) -> Result { Ok(args.process_raw(registry, save)?.run()) } } fn save( SaveArgs { path, raw: save_raw, }: SaveArgs, RunnableContext { input, name, shell_manager, source_map, host, commands: registry, .. }: RunnableContext, raw_args: RawCommandArgs, ) -> Result { let mut full_path = PathBuf::from(shell_manager.path()); let name_span = name; let source_map = source_map.clone(); let stream = async_stream_block! { let input: Vec> = input.values.collect().await; if path.is_none() { // If there is no filename, check the metadata for the origin filename if input.len() > 0 { let origin = input[0].origin(); match origin.and_then(|x| source_map.get(&x)) { Some(path) => match path { SpanSource::File(file) => { full_path.push(Path::new(file)); } _ => { yield Err(ShellError::labeled_error( "Save requires a filepath", "needs path", name_span, )); } }, None => { yield Err(ShellError::labeled_error( "Save requires a filepath", "needs path", name_span, )); } } } else { yield Err(ShellError::labeled_error( "Save requires a filepath", "needs path", name_span, )); } } else { if let Some(file) = path { full_path.push(file.item()); } } let content = if !save_raw { if let Some(extension) = full_path.extension() { let command_name = format!("to-{}", extension.to_str().unwrap()); if let Some(converter) = registry.get_command(&command_name) { let new_args = RawCommandArgs { host: host, shell_manager: shell_manager, call_info: UnevaluatedCallInfo { args: crate::parser::hir::Call { head: raw_args.call_info.args.head, positional: None, named: None }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, name_span: raw_args.call_info.name_span, } }; let mut result = converter.run(new_args.with_input(input), ®istry); let result_vec: Vec> = result.drain_vec().await; let mut result_string = String::new(); for res in result_vec { match res { Ok(ReturnSuccess::Value(Tagged { item: Value::Primitive(Primitive::String(s)), .. })) => { result_string.push_str(&s); } _ => { yield Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during save", name_span, )); }, } } Ok(result_string) } else { let mut result_string = String::new(); for res in input { match res { Tagged { item: Value::Primitive(Primitive::String(s)), .. } => { result_string.push_str(&s); } _ => { yield Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during save", name_span, )); }, } } Ok(result_string) } } else { let mut result_string = String::new(); for res in input { match res { Tagged { item: Value::Primitive(Primitive::String(s)), .. } => { result_string.push_str(&s); } _ => { yield Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during save", name_span, )); }, } } Ok(result_string) } } else { string_from(&input) }; match content { Ok(save_data) => match std::fs::write(full_path, save_data) { Ok(o) => o, Err(e) => yield Err(ShellError::string(e.to_string())), }, Err(e) => yield Err(ShellError::string(e.to_string())), } }; Ok(OutputStream::new(stream)) } fn string_from(input: &Vec>) -> Result { let mut save_data = String::new(); if input.len() > 0 { let mut first = true; for i in input.iter() { if !first { save_data.push_str("\n"); } else { first = false; } if let Ok(data) = &i.as_string() { save_data.push_str(data); } } } Ok(save_data) }