mirror of
https://github.com/nushell/nushell
synced 2024-12-27 13:33:16 +00:00
Port network/url
command (#452)
* feat: add url command * feat(network/url): add sub-command for url
This commit is contained in:
parent
5c27ffa42e
commit
c3b6e07de6
14 changed files with 424 additions and 3 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1610,6 +1610,7 @@ dependencies = [
|
|||
"toml",
|
||||
"trash",
|
||||
"unicode-segmentation",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ nu-parser = { path = "../nu-parser" }
|
|||
nu-ansi-term = { path = "../nu-ansi-term" }
|
||||
|
||||
# Potential dependencies for extras
|
||||
url = "2.2.1"
|
||||
csv = "1.1.3"
|
||||
glob = "0.3.0"
|
||||
Inflector = "0.11"
|
||||
|
|
|
@ -7,7 +7,7 @@ use nu_protocol::{
|
|||
|
||||
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) {
|
||||
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(Math));
|
||||
working_set.add_decl(Box::new(Date));
|
||||
working_set.add_decl(Box::new(Url));
|
||||
|
||||
use super::Echo;
|
||||
working_set.add_decl(Box::new(Echo));
|
||||
|
|
|
@ -17,6 +17,7 @@ mod yaml;
|
|||
|
||||
pub use self::csv::FromCsv;
|
||||
pub use self::toml::FromToml;
|
||||
pub use self::url::FromUrl;
|
||||
pub use command::From;
|
||||
pub use eml::FromEml;
|
||||
pub use ics::FromIcs;
|
||||
|
@ -25,7 +26,6 @@ pub use json::FromJson;
|
|||
pub use ods::FromOds;
|
||||
pub use ssv::FromSsv;
|
||||
pub use tsv::FromTsv;
|
||||
pub use url::FromUrl;
|
||||
pub use vcf::FromVcf;
|
||||
pub use xlsx::FromXlsx;
|
||||
pub use xml::FromXml;
|
||||
|
|
|
@ -8,7 +8,7 @@ mod url;
|
|||
|
||||
pub use self::csv::ToCsv;
|
||||
pub use self::toml::ToToml;
|
||||
pub use self::url::ToUrl;
|
||||
pub use command::To;
|
||||
pub use json::ToJson;
|
||||
pub use tsv::ToTsv;
|
||||
pub use url::ToUrl;
|
||||
|
|
|
@ -10,6 +10,7 @@ mod filesystem;
|
|||
mod filters;
|
||||
mod formats;
|
||||
mod math;
|
||||
mod network;
|
||||
mod platform;
|
||||
mod random;
|
||||
mod shells;
|
||||
|
@ -29,6 +30,7 @@ pub use filesystem::*;
|
|||
pub use filters::*;
|
||||
pub use formats::*;
|
||||
pub use math::*;
|
||||
pub use network::*;
|
||||
pub use platform::*;
|
||||
pub use random::*;
|
||||
pub use shells::*;
|
||||
|
|
3
crates/nu-command/src/network/mod.rs
Normal file
3
crates/nu-command/src/network/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
mod url;
|
||||
|
||||
pub use self::url::*;
|
37
crates/nu-command/src/network/url/command.rs
Normal file
37
crates/nu-command/src/network/url/command.rs
Normal 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())
|
||||
}
|
||||
}
|
65
crates/nu-command/src/network/url/host.rs
Normal file
65
crates/nu-command/src/network/url/host.rs
Normal 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 {})
|
||||
}
|
||||
}
|
92
crates/nu-command/src/network/url/mod.rs
Normal file
92
crates/nu-command/src/network/url/mod.rs
Normal 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(),
|
||||
)
|
||||
}
|
71
crates/nu-command/src/network/url/path.rs
Normal file
71
crates/nu-command/src/network/url/path.rs
Normal 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 {})
|
||||
}
|
||||
}
|
75
crates/nu-command/src/network/url/query.rs
Normal file
75
crates/nu-command/src/network/url/query.rs
Normal 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 {})
|
||||
}
|
||||
}
|
71
crates/nu-command/src/network/url/scheme.rs
Normal file
71
crates/nu-command/src/network/url/scheme.rs
Normal 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 {})
|
||||
}
|
||||
}
|
|
@ -42,6 +42,7 @@ pub enum Category {
|
|||
Filters,
|
||||
Formats,
|
||||
Math,
|
||||
Network,
|
||||
Random,
|
||||
Platform,
|
||||
Shells,
|
||||
|
@ -64,6 +65,7 @@ impl std::fmt::Display for Category {
|
|||
Category::Filters => "filters",
|
||||
Category::Formats => "formats",
|
||||
Category::Math => "math",
|
||||
Category::Network => "network",
|
||||
Category::Random => "random",
|
||||
Category::Platform => "platform",
|
||||
Category::Shells => "shells",
|
||||
|
|
Loading…
Reference in a new issue