Port network/url command (#452)

* feat: add url command

* feat(network/url): add sub-command for url
This commit is contained in:
Jae-Heon Ji 2021-12-10 09:09:30 +09:00 committed by GitHub
parent 5c27ffa42e
commit c3b6e07de6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 424 additions and 3 deletions

1
Cargo.lock generated
View File

@ -1610,6 +1610,7 @@ dependencies = [
"toml", "toml",
"trash", "trash",
"unicode-segmentation", "unicode-segmentation",
"url",
"uuid", "uuid",
] ]

View File

@ -16,6 +16,7 @@ nu-parser = { path = "../nu-parser" }
nu-ansi-term = { path = "../nu-ansi-term" } nu-ansi-term = { path = "../nu-ansi-term" }
# Potential dependencies for extras # Potential dependencies for extras
url = "2.2.1"
csv = "1.1.3" csv = "1.1.3"
glob = "0.3.0" glob = "0.3.0"
Inflector = "0.11" Inflector = "0.11"

View File

@ -7,7 +7,7 @@ use nu_protocol::{
use crate::To; use crate::To;
use super::{Date, From, Into, Math, Random, Split, Str}; use super::{Date, From, Into, Math, Random, Split, Str, Url};
pub fn test_examples(cmd: impl Command + 'static) { pub fn test_examples(cmd: impl Command + 'static) {
let examples = cmd.examples(); let examples = cmd.examples();
@ -25,6 +25,7 @@ pub fn test_examples(cmd: impl Command + 'static) {
working_set.add_decl(Box::new(Split)); working_set.add_decl(Box::new(Split));
working_set.add_decl(Box::new(Math)); working_set.add_decl(Box::new(Math));
working_set.add_decl(Box::new(Date)); working_set.add_decl(Box::new(Date));
working_set.add_decl(Box::new(Url));
use super::Echo; use super::Echo;
working_set.add_decl(Box::new(Echo)); working_set.add_decl(Box::new(Echo));

View File

@ -17,6 +17,7 @@ mod yaml;
pub use self::csv::FromCsv; pub use self::csv::FromCsv;
pub use self::toml::FromToml; pub use self::toml::FromToml;
pub use self::url::FromUrl;
pub use command::From; pub use command::From;
pub use eml::FromEml; pub use eml::FromEml;
pub use ics::FromIcs; pub use ics::FromIcs;
@ -25,7 +26,6 @@ pub use json::FromJson;
pub use ods::FromOds; pub use ods::FromOds;
pub use ssv::FromSsv; pub use ssv::FromSsv;
pub use tsv::FromTsv; pub use tsv::FromTsv;
pub use url::FromUrl;
pub use vcf::FromVcf; pub use vcf::FromVcf;
pub use xlsx::FromXlsx; pub use xlsx::FromXlsx;
pub use xml::FromXml; pub use xml::FromXml;

View File

@ -8,7 +8,7 @@ mod url;
pub use self::csv::ToCsv; pub use self::csv::ToCsv;
pub use self::toml::ToToml; pub use self::toml::ToToml;
pub use self::url::ToUrl;
pub use command::To; pub use command::To;
pub use json::ToJson; pub use json::ToJson;
pub use tsv::ToTsv; pub use tsv::ToTsv;
pub use url::ToUrl;

View File

@ -10,6 +10,7 @@ mod filesystem;
mod filters; mod filters;
mod formats; mod formats;
mod math; mod math;
mod network;
mod platform; mod platform;
mod random; mod random;
mod shells; mod shells;
@ -29,6 +30,7 @@ pub use filesystem::*;
pub use filters::*; pub use filters::*;
pub use formats::*; pub use formats::*;
pub use math::*; pub use math::*;
pub use network::*;
pub use platform::*; pub use platform::*;
pub use random::*; pub use random::*;
pub use shells::*; pub use shells::*;

View File

@ -0,0 +1,3 @@
mod url;
pub use self::url::*;

View File

@ -0,0 +1,37 @@
use nu_engine::get_full_help;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, IntoPipelineData, PipelineData, Signature, Value,
};
#[derive(Clone)]
pub struct Url;
impl Command for Url {
fn name(&self) -> &str {
"url"
}
fn signature(&self) -> Signature {
Signature::build("url").category(Category::Network)
}
fn usage(&self) -> &str {
"Apply url function."
}
fn run(
&self,
engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(Value::String {
val: get_full_help(&Url.signature(), &Url.examples(), engine_state),
span: call.head,
}
.into_pipeline_data())
}
}

View File

@ -0,0 +1,65 @@
use super::{operator, url};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"url host"
}
fn signature(&self) -> Signature {
Signature::build("url host")
.rest(
"rest",
SyntaxShape::CellPath,
"optionally operate by cell path",
)
.category(Category::Network)
}
fn usage(&self) -> &str {
"gets the host of a url"
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
operator(engine_state, stack, call, input, &host)
}
fn examples(&self) -> Vec<Example> {
let span = Span::unknown();
vec![Example {
description: "Get host of a url",
example: "echo 'http://www.example.com/foo/bar' | url host",
result: Some(Value::String {
val: "www.example.com".to_string(),
span,
}),
}]
}
}
fn host(url: &url::Url) -> &str {
url.host_str().unwrap_or("")
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@ -0,0 +1,92 @@
mod command;
mod host;
mod path;
mod query;
mod scheme;
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
engine::{EngineState, Stack},
PipelineData, ShellError, Span, Value,
};
use url::{self};
pub use self::host::SubCommand as UrlHost;
pub use self::path::SubCommand as UrlPath;
pub use self::query::SubCommand as UrlQuery;
pub use self::scheme::SubCommand as UrlScheme;
pub use command::Url;
fn handle_value<F>(action: &F, v: &Value, span: Span) -> Value
where
F: Fn(&url::Url) -> &str + Send + 'static,
{
let a = |url| Value::String {
val: action(url).to_string(),
span,
};
match v {
Value::String { val: s, .. } => {
let s = s.trim();
match url::Url::parse(s) {
Ok(url) => a(&url),
Err(_) => Value::String {
val: "".to_string(),
span,
},
}
}
other => {
let span = other.span();
match span {
Ok(s) => {
let got = format!("Expected a string, got {} instead", other.get_type());
Value::Error {
error: ShellError::UnsupportedInput(got, s),
}
}
Err(e) => Value::Error { error: e },
}
}
}
}
fn operator<F>(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
action: &'static F,
) -> Result<PipelineData, ShellError>
where
F: Fn(&url::Url) -> &str + Send + Sync + 'static,
{
let span = call.head;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
input.map(
move |v| {
if column_paths.is_empty() {
handle_value(&action, &v, span)
} else {
let mut ret = v;
for path in &column_paths {
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| handle_value(&action, old, span)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}

View File

@ -0,0 +1,71 @@
use super::{operator, url};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"url path"
}
fn signature(&self) -> Signature {
Signature::build("url path")
.rest(
"rest",
SyntaxShape::CellPath,
"optionally operate by cell path",
)
.category(Category::Network)
}
fn usage(&self) -> &str {
"gets the path of a url"
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
operator(engine_state, stack, call, input, &url::Url::path)
}
fn examples(&self) -> Vec<Example> {
let span = Span::unknown();
vec![
Example {
description: "Get path of a url",
example: "echo 'http://www.example.com/foo/bar' | url path",
result: Some(Value::String {
val: "/foo/bar".to_string(),
span,
}),
},
Example {
description: "A trailing slash will be reflected in the path",
example: "echo 'http://www.example.com' | url path",
result: Some(Value::String {
val: "/".to_string(),
span,
}),
},
]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@ -0,0 +1,75 @@
use super::{operator, url};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"url query"
}
fn signature(&self) -> Signature {
Signature::build("url query")
.rest(
"rest",
SyntaxShape::CellPath,
"optionally operate by cell path",
)
.category(Category::Network)
}
fn usage(&self) -> &str {
"gets the query of a url"
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
operator(engine_state, stack, call, input, &query)
}
fn examples(&self) -> Vec<Example> {
let span = Span::unknown();
vec![
Example {
description: "Get query of a url",
example: "echo 'http://www.example.com/?foo=bar&baz=quux' | url query",
result: Some(Value::String {
val: "foo=bar&baz=quux".to_string(),
span,
}),
},
Example {
description: "No query gives the empty string",
example: "echo 'http://www.example.com/' | url query",
result: Some(Value::String {
val: "".to_string(),
span,
}),
},
]
}
}
fn query(url: &url::Url) -> &str {
url.query().unwrap_or("")
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@ -0,0 +1,71 @@
use super::{operator, url};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"url scheme"
}
fn signature(&self) -> Signature {
Signature::build("url scheme")
.rest(
"rest",
SyntaxShape::CellPath,
"optionally operate by cell path",
)
.category(Category::Network)
}
fn usage(&self) -> &str {
"gets the scheme (eg http, file) of a url"
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
operator(engine_state, stack, call, input, &url::Url::scheme)
}
fn examples(&self) -> Vec<Example> {
let span = Span::unknown();
vec![
Example {
description: "Get scheme of a url",
example: "echo 'http://www.example.com' | url scheme",
result: Some(Value::String {
val: "http".to_string(),
span,
}),
},
Example {
description: "You get an empty string if there is no scheme",
example: "echo 'test' | url scheme",
result: Some(Value::String {
val: "".to_string(),
span,
}),
},
]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@ -42,6 +42,7 @@ pub enum Category {
Filters, Filters,
Formats, Formats,
Math, Math,
Network,
Random, Random,
Platform, Platform,
Shells, Shells,
@ -64,6 +65,7 @@ impl std::fmt::Display for Category {
Category::Filters => "filters", Category::Filters => "filters",
Category::Formats => "formats", Category::Formats => "formats",
Category::Math => "math", Category::Math => "math",
Category::Network => "network",
Category::Random => "random", Category::Random => "random",
Category::Platform => "platform", Category::Platform => "platform",
Category::Shells => "shells", Category::Shells => "shells",