From cfcf43ca933d2bf4f15b4ad54acbd0a78b184819 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 26 Jun 2019 19:40:43 +1200 Subject: [PATCH] Add content-type to open, refactor open/enter --- Cargo.lock | 1 + Cargo.toml | 1 + src/commands/enter.rs | 168 +++-------------------- src/commands/from_yaml.rs | 3 +- src/commands/open.rs | 276 +++++++++++++++++++------------------- 5 files changed, 160 insertions(+), 289 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 63e61c301c..2d2489ac1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1594,6 +1594,7 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "logos 0.10.0-rc2 (registry+https://github.com/rust-lang/crates.io-index)", "logos-derive 0.10.0-rc2 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.0-beta3 (registry+https://github.com/rust-lang/crates.io-index)", "nom_locate 0.3.1 (git+https://github.com/wycats/nom_locate.git?branch=nom5)", "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 7f0f858f3d..140d88adc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,7 @@ unicode-xid = "0.1.0" serde_ini = "0.2.0" subprocess = "0.1.18" sys-info = "0.5.7" +mime = "0.3.13" [dependencies.pancurses] version = "0.16" diff --git a/src/commands/enter.rs b/src/commands/enter.rs index ef6cdf9992..d699db5d01 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -1,20 +1,18 @@ use crate::commands::command::CommandAction; +use crate::commands::open::{fetch, parse_as_value}; use crate::errors::ShellError; use crate::object::{Primitive, Value}; use crate::prelude::*; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; pub fn enter(args: CommandArgs) -> Result { - let path = match args.nth(0) { - None => { - return Err(ShellError::maybe_labeled_error( - "open requires a path or url", - "missing path", - args.name_span, - )) - } - Some(p) => p, - }; + if args.len() == 0 { + return Err(ShellError::maybe_labeled_error( + "open requires a path or url", + "missing path", + args.name_span, + )); + } let span = args.name_span; @@ -27,62 +25,10 @@ pub fn enter(args: CommandArgs) -> Result { .path() .to_path_buf(); - let mut full_path = PathBuf::from(cwd); + let full_path = PathBuf::from(cwd); - let (file_extension, contents) = match path.item() { - Value::Primitive(Primitive::String(s)) => { - if s.starts_with("http:") || s.starts_with("https:") { - let response = reqwest::get(s); - match response { - Ok(mut r) => match r.text() { - Ok(s) => { - let fname = r - .url() - .path_segments() - .and_then(|segments| segments.last()) - .and_then(|name| if name.is_empty() { None } else { Some(name) }) - .and_then(|name| { - PathBuf::from(name) - .extension() - .map(|name| name.to_string_lossy().to_string()) - }); - (fname, s) - } - Err(_) => { - return Err(ShellError::labeled_error( - "Web page contents corrupt", - "received garbled data", - args.expect_nth(0)?.span, - )); - } - }, - Err(_) => { - return Err(ShellError::labeled_error( - "URL could not be opened", - "url not found", - args.expect_nth(0)?.span, - )); - } - } - } else { - full_path.push(Path::new(s)); - match std::fs::read_to_string(&full_path) { - Ok(s) => ( - full_path - .extension() - .map(|name| name.to_string_lossy().to_string()), - s, - ), - Err(_) => { - return Err(ShellError::labeled_error( - "File cound not be opened", - "file not found", - args.expect_nth(0)?.span, - )); - } - } - } - } + let (file_extension, contents) = match &args.expect_nth(0)?.item { + Value::Primitive(Primitive::String(s)) => fetch(&full_path, s, args.expect_nth(0)?.span)?, _ => { return Err(ShellError::labeled_error( "Expected string value for filename", @@ -121,91 +67,11 @@ pub fn enter(args: CommandArgs) -> Result { } }; - match file_extension { - Some(x) if x == "toml" => { - stream.push_back(ReturnValue::Action(CommandAction::Enter( - crate::commands::from_toml::from_toml_string_to_value(contents).map_err( - move |_| { - ShellError::maybe_labeled_error( - "Could not load as TOML", - "could not load as TOML", - span, - ) - }, - )?, - ))); - } - Some(x) if x == "json" => { - stream.push_back(ReturnValue::Action(CommandAction::Enter( - crate::commands::from_json::from_json_string_to_value(contents).map_err( - move |_| { - ShellError::maybe_labeled_error( - "Could not load as JSON", - "could not load as JSON", - span, - ) - }, - )?, - ))); - } - Some(x) if x == "xml" => { - stream.push_back(ReturnValue::Action(CommandAction::Enter( - crate::commands::from_xml::from_xml_string_to_value(contents).map_err( - move |_| { - ShellError::maybe_labeled_error( - "Could not load as XML", - "could not load as XML", - span, - ) - }, - )?, - ))); - } - Some(x) if x == "ini" => { - stream.push_back(ReturnValue::Action(CommandAction::Enter( - crate::commands::from_ini::from_ini_string_to_value(contents).map_err( - move |_| { - ShellError::maybe_labeled_error( - "Could not load as INI", - "could not load as INI", - span, - ) - }, - )?, - ))); - } - Some(x) if x == "yml" => { - stream.push_back(ReturnValue::Action(CommandAction::Enter( - crate::commands::from_yaml::from_yaml_string_to_value(contents).map_err( - move |_| { - ShellError::maybe_labeled_error( - "Could not load as YAML", - "could not load as YAML", - span, - ) - }, - )?, - ))); - } - Some(x) if x == "yaml" => { - stream.push_back(ReturnValue::Action(CommandAction::Enter( - crate::commands::from_yaml::from_yaml_string_to_value(contents).map_err( - move |_| { - ShellError::maybe_labeled_error( - "Could not load as YAML", - "could not load as YAML", - span, - ) - }, - )?, - ))); - } - _ => { - stream.push_back(ReturnValue::Action(CommandAction::Enter(Value::Primitive( - Primitive::String(contents), - )))); - } - } + stream.push_back(ReturnValue::Action(CommandAction::Enter(parse_as_value( + file_extension, + contents, + span, + )?))); Ok(stream.boxed()) } diff --git a/src/commands/from_yaml.rs b/src/commands/from_yaml.rs index 2363dd7fd4..6aa5ab0af5 100644 --- a/src/commands/from_yaml.rs +++ b/src/commands/from_yaml.rs @@ -32,7 +32,8 @@ fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value) -> Value { } Value::Object(collected) } - _ => unimplemented!("Unsupported yaml case"), + serde_yaml::Value::Null => Value::Primitive(Primitive::Nothing), + x => unimplemented!("Unsupported yaml case: {:?}", x), } } diff --git a/src/commands/open.rs b/src/commands/open.rs index d3b0afafd6..87d1d959e7 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -1,9 +1,12 @@ use crate::errors::ShellError; use crate::object::{Primitive, Value}; +use crate::parser::parse::span::Span; use crate::parser::registry::{CommandConfig, NamedType}; use crate::prelude::*; use indexmap::IndexMap; +use mime::Mime; use std::path::{Path, PathBuf}; +use std::str::FromStr; pub struct Open; @@ -29,6 +32,134 @@ impl Command for Open { } } +pub fn fetch(cwd: &PathBuf, location: &str, span: Span) -> Result<(Option, String), ShellError> { + let mut cwd = cwd.clone(); + if location.starts_with("http:") || location.starts_with("https:") { + let response = reqwest::get(location); + match response { + Ok(mut r) => match r.text() { + Ok(s) => { + let path_extension = r + .url() + .path_segments() + .and_then(|segments| segments.last()) + .and_then(|name| if name.is_empty() { None } else { Some(name) }) + .and_then(|name| { + PathBuf::from(name) + .extension() + .map(|name| name.to_string_lossy().to_string()) + }); + + let extension = match r.headers().get("content-type") { + Some(content_type) => { + let content_type = + Mime::from_str(content_type.to_str().unwrap()).unwrap(); + match (content_type.type_(), content_type.subtype()) { + (mime::APPLICATION, mime::XML) => Some("xml".to_string()), + (mime::APPLICATION, mime::JSON) => Some("json".to_string()), + _ => path_extension, + } + } + None => path_extension, + }; + + Ok((extension, s)) + } + Err(_) => { + return Err(ShellError::labeled_error( + "Web page contents corrupt", + "received garbled data", + span, + )); + } + }, + Err(_) => { + return Err(ShellError::labeled_error( + "URL could not be opened", + "url not found", + span, + )); + } + } + } else { + cwd.push(Path::new(location)); + match std::fs::read_to_string(&cwd) { + Ok(s) => Ok(( + cwd.extension() + .map(|name| name.to_string_lossy().to_string()), + s, + )), + Err(_) => { + return Err(ShellError::labeled_error( + "File cound not be opened", + "file not found", + span, + )); + } + } + } +} + +pub fn parse_as_value( + extension: Option, + contents: String, + name_span: Option, +) -> Result { + match extension { + Some(x) if x == "toml" => crate::commands::from_toml::from_toml_string_to_value(contents) + .map_err(move |_| { + ShellError::maybe_labeled_error( + "Could not open as TOML", + "could not open as TOML", + name_span, + ) + }), + Some(x) if x == "json" => crate::commands::from_json::from_json_string_to_value(contents) + .map_err(move |_| { + ShellError::maybe_labeled_error( + "Could not open as JSON", + "could not open as JSON", + name_span, + ) + }), + Some(x) if x == "ini" => crate::commands::from_ini::from_ini_string_to_value(contents) + .map_err(move |_| { + ShellError::maybe_labeled_error( + "Could not open as INI", + "could not open as INI", + name_span, + ) + }), + Some(x) if x == "xml" => crate::commands::from_xml::from_xml_string_to_value(contents) + .map_err(move |_| { + ShellError::maybe_labeled_error( + "Could not open as XML", + "could not open as XML", + name_span, + ) + }), + Some(x) if x == "yml" => crate::commands::from_yaml::from_yaml_string_to_value(contents) + .map_err(move |_| { + ShellError::maybe_labeled_error( + "Could not open as YAML", + "could not open as YAML", + name_span, + ) + }), + Some(x) if x == "yaml" => crate::commands::from_yaml::from_yaml_string_to_value(contents) + .map_err(move |_| { + ShellError::maybe_labeled_error( + "Could not open as YAML", + "could not open as YAML", + name_span, + ) + }), + _ => { + Ok(Value::string(contents)) + } + } +} + fn open(args: CommandArgs) -> Result { if args.len() == 0 { return Err(ShellError::maybe_labeled_error( @@ -48,62 +179,10 @@ fn open(args: CommandArgs) -> Result { .unwrap() .path() .to_path_buf(); - let mut full_path = PathBuf::from(cwd); + let full_path = PathBuf::from(cwd); let (file_extension, contents) = match &args.expect_nth(0)?.item { - Value::Primitive(Primitive::String(s)) => { - if s.starts_with("http:") || s.starts_with("https:") { - let response = reqwest::get(s); - match response { - Ok(mut r) => match r.text() { - Ok(s) => { - let fname = r - .url() - .path_segments() - .and_then(|segments| segments.last()) - .and_then(|name| if name.is_empty() { None } else { Some(name) }) - .and_then(|name| { - PathBuf::from(name) - .extension() - .map(|name| name.to_string_lossy().to_string()) - }); - (fname, s) - } - Err(_) => { - return Err(ShellError::labeled_error( - "Web page contents corrupt", - "received garbled data", - args.expect_nth(0)?.span, - )); - } - }, - Err(_) => { - return Err(ShellError::labeled_error( - "URL could not be opened", - "url not found", - args.expect_nth(0)?.span, - )); - } - } - } else { - full_path.push(Path::new(s)); - match std::fs::read_to_string(&full_path) { - Ok(s) => ( - full_path - .extension() - .map(|name| name.to_string_lossy().to_string()), - s, - ), - Err(_) => { - return Err(ShellError::labeled_error( - "File cound not be opened", - "file not found", - args.expect_nth(0)?.span, - )); - } - } - } - } + Value::Primitive(Primitive::String(s)) => fetch(&full_path, s, args.expect_nth(0)?.span)?, _ => { return Err(ShellError::labeled_error( "Expected string value for filename", @@ -141,89 +220,12 @@ fn open(args: CommandArgs) -> Result { file_extension } }; - match file_extension { - Some(x) if x == "toml" => { - stream.push_back(ReturnValue::Value( - crate::commands::from_toml::from_toml_string_to_value(contents).map_err( - move |_| { - ShellError::maybe_labeled_error( - "Could not open as TOML", - "could not open as TOML", - span, - ) - }, - )?, - )); - } - Some(x) if x == "json" => { - stream.push_back(ReturnValue::Value( - crate::commands::from_json::from_json_string_to_value(contents).map_err( - move |_| { - ShellError::maybe_labeled_error( - "Could not open as JSON", - "could not open as JSON", - span, - ) - }, - )?, - )); - } - Some(x) if x == "ini" => { - stream.push_back(ReturnValue::Value( - crate::commands::from_ini::from_ini_string_to_value(contents).map_err( - move |_| { - ShellError::maybe_labeled_error( - "Could not open as INI", - "could not open as INI", - span, - ) - }, - )?, - )); - } - Some(x) if x == "xml" => { - stream.push_back(ReturnValue::Value( - crate::commands::from_xml::from_xml_string_to_value(contents).map_err( - move |_| { - ShellError::maybe_labeled_error( - "Could not open as XML", - "could not open as XML", - span, - ) - }, - )?, - )); - } - Some(x) if x == "yml" => { - stream.push_back(ReturnValue::Value( - crate::commands::from_yaml::from_yaml_string_to_value(contents).map_err( - move |_| { - ShellError::maybe_labeled_error( - "Could not open as YAML", - "could not open as YAML", - span, - ) - }, - )?, - )); - } - Some(x) if x == "yaml" => { - stream.push_back(ReturnValue::Value( - crate::commands::from_yaml::from_yaml_string_to_value(contents).map_err( - move |_| { - ShellError::maybe_labeled_error( - "Could not open as YAML", - "could not open as YAML", - span, - ) - }, - )?, - )); - } - _ => { - stream.push_back(ReturnValue::Value(Value::string(contents))); - } - } + + stream.push_back(ReturnValue::Value(parse_as_value( + file_extension, + contents, + span, + )?)); Ok(stream.boxed()) }