From ad18c7f61a80af394630b7d6c13f8ccccc051c26 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sat, 31 Aug 2019 16:08:59 +1200 Subject: [PATCH] Finish magic post and magic receive --- src/commands/post.rs | 174 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 151 insertions(+), 23 deletions(-) diff --git a/src/commands/post.rs b/src/commands/post.rs index 7a6d99774f..adab330248 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -1,12 +1,13 @@ +use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; use crate::errors::ShellError; -use crate::object::{Primitive, Value}; +use crate::object::Value; use crate::parser::hir::SyntaxType; use crate::parser::registry::Signature; use crate::prelude::*; use base64::encode; use mime::Mime; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::str::FromStr; use surf::mime; use uuid::Uuid; @@ -29,51 +30,106 @@ impl PerItemCommand for Post { fn run( &self, call_info: &CallInfo, - _registry: &CommandRegistry, - shell_manager: &ShellManager, + registry: &CommandRegistry, + raw_args: &RawCommandArgs, _input: Tagged, ) -> Result { - run(call_info, shell_manager) + run(call_info, registry, raw_args) } } -fn run(call_info: &CallInfo, shell_manager: &ShellManager) -> Result { - let cwd = PathBuf::from(shell_manager.path()); - let full_path = PathBuf::from(cwd); - +fn run( + call_info: &CallInfo, + registry: &CommandRegistry, + raw_args: &RawCommandArgs, +) -> Result { + let call_info = call_info.clone(); let path = match call_info .args .nth(0) - .ok_or_else(|| ShellError::string(&format!("No file or directory specified")))? + .ok_or_else(|| ShellError::string(&format!("No url specified")))? { - file => file, + file => file.clone(), }; let body = match call_info .args .nth(1) .ok_or_else(|| ShellError::string(&format!("No body specified")))? { - file => file, + file => file.clone(), }; let path_str = path.as_string()?; - let body_str = body.as_string()?; + //let body_str = body.as_string()?; let path_span = path.span(); - let name_span = call_info.name_span; let has_raw = call_info.args.has("raw"); let user = call_info.args.get("user").map(|x| x.as_string().unwrap()); let password = call_info .args .get("password") .map(|x| x.as_string().unwrap()); + let registry = registry.clone(); + let raw_args = raw_args.clone(); - //r#"{"query": "query { viewer { name, } }"}"#.to_string() let stream = async_stream_block! { let (file_extension, contents, contents_tag, span_source) = - post(&path_str, body_str, user, password, path_span).await.unwrap(); + post(&path_str, &body, user, password, path_span, ®istry, &raw_args).await.unwrap(); - //println!("{:?}", contents); + let file_extension = if has_raw { + None + } else { + // If the extension could not be determined via mimetype, try to use the path + // extension. Some file types do not declare their mimetypes (such as bson files). + file_extension.or(path_str.split('.').last().map(String::from)) + }; - yield ReturnSuccess::value(contents.tagged(contents_tag)); + if let Some(uuid) = contents_tag.origin { + // If we have loaded something, track its source + yield ReturnSuccess::action(CommandAction::AddSpanSource( + uuid, + span_source, + )); + } + + let tagged_contents = contents.tagged(contents_tag); + + if let Some(extension) = file_extension { + let command_name = format!("from-{}", extension); + if let Some(converter) = registry.get_command(&command_name) { + let new_args = RawCommandArgs { + host: raw_args.host, + shell_manager: raw_args.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(vec![tagged_contents]), ®istry); + let result_vec: Vec> = result.drain_vec().await; + for res in result_vec { + match res { + Ok(ReturnSuccess::Value(Tagged { item: Value::List(list), ..})) => { + for l in list { + yield Ok(ReturnSuccess::Value(l)); + } + } + Ok(ReturnSuccess::Value(Tagged { item, .. })) => { + yield Ok(ReturnSuccess::Value(Tagged { item: item, tag: contents_tag })); + } + x => yield x, + } + } + } else { + yield ReturnSuccess::value(tagged_contents); + } + } else { + yield ReturnSuccess::value(tagged_contents); + } }; Ok(stream.to_output_stream()) @@ -81,17 +137,89 @@ fn run(call_info: &CallInfo, shell_manager: &ShellManager) -> Result, user: Option, password: Option, span: Span, + registry: &CommandRegistry, + raw_args: &RawCommandArgs, ) -> Result<(Option, Value, Tag, SpanSource), ShellError> { + let registry = registry.clone(); + let raw_args = raw_args.clone(); if location.starts_with("http:") || location.starts_with("https:") { let login = encode(&format!("{}:{}", user.unwrap(), password.unwrap())); - let response = surf::post(location) - .body_string(body) - .set_header("Authorization", format!("Basic {}", login)) - .await; + let response = match body { + Tagged { + item: Value::Primitive(Primitive::String(body_str)), + .. + } => { + surf::post(location) + .body_string(body_str.to_string()) + .set_header("Authorization", format!("Basic {}", login)) + .await + } + Tagged { + item: Value::Binary(b), + .. + } => { + surf::post(location) + .body_bytes(b) + .set_header("Authorization", format!("Basic {}", login)) + .await + } + Tagged { item, tag } => { + if let Some(converter) = registry.get_command("to-json") { + let new_args = RawCommandArgs { + host: raw_args.host, + shell_manager: raw_args.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(vec![item.clone().tagged(tag.clone())]), + ®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); + } + _ => { + return Err(ShellError::labeled_error( + "Save could not successfully save", + "unexpected data during save", + span, + )); + } + } + } + surf::post(location) + .body_string(result_string) + .set_header("Authorization", format!("Basic {}", login)) + .await + } else { + return Err(ShellError::labeled_error( + "Could not automatically convert table", + "needs manual conversion", + tag.span, + )); + } + } + }; match response { Ok(mut r) => match r.headers().get("content-type") { Some(content_type) => {