mirror of
https://github.com/nushell/nushell
synced 2024-12-26 13:03:07 +00:00
provide a common implementation for query string conversions in url join
and url build-query
(#14173)
Addresses one of the points in #14162 # Description Factors out part of the `url::build_query::to_url` function into a separate function `url::query::record_to_qs()`, which is then used in both `url::build_query` and `url::join`. # User-Facing Changes Like with `url build-query` (after #14073), `url join` will allow list values in `params` and behavior of two commands will be same. ```nushell > {a: ["one", "two"], b: "three"} | url build-query "a=one&a=two&b=three" > {scheme: "http", host: "host", params: {a: ["one", "two"], b: "three"}} | url join "http://host?a=one&a=two&b=three" ``` # Tests + Formatting Added an example to `url join` for the new behavior.
This commit is contained in:
parent
9ebaa737aa
commit
719d9aa83c
4 changed files with 70 additions and 55 deletions
|
@ -1,5 +1,7 @@
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
use super::query::record_to_query_string;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
|
@ -38,12 +40,12 @@ impl Command for SubCommand {
|
||||||
result: Some(Value::test_string("foo=1&bar=2")),
|
result: Some(Value::test_string("foo=1&bar=2")),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Outputs a query string representing the contents of this record",
|
description: "Outputs a query string representing the contents of this record, with a value that needs to be url-encoded",
|
||||||
example: r#"{a:"AT&T", b: "AT T"} | url build-query"#,
|
example: r#"{a:"AT&T", b: "AT T"} | url build-query"#,
|
||||||
result: Some(Value::test_string("a=AT%26T&b=AT+T")),
|
result: Some(Value::test_string("a=AT%26T&b=AT+T")),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Outputs a query string representing the contents of this record",
|
description: "Outputs a query string representing the contents of this record, \"exploding\" the list into multiple parameters",
|
||||||
example: r#"{a: ["one", "two"], b: "three"} | url build-query"#,
|
example: r#"{a: ["one", "two"], b: "three"} | url build-query"#,
|
||||||
result: Some(Value::test_string("a=one&a=two&b=three")),
|
result: Some(Value::test_string("a=one&a=two&b=three")),
|
||||||
},
|
},
|
||||||
|
@ -68,48 +70,7 @@ fn to_url(input: PipelineData, head: Span) -> Result<PipelineData, ShellError> {
|
||||||
.map(move |value| {
|
.map(move |value| {
|
||||||
let span = value.span();
|
let span = value.span();
|
||||||
match value {
|
match value {
|
||||||
Value::Record { ref val, .. } => {
|
Value::Record { ref val, .. } => record_to_query_string(val, span, head),
|
||||||
let mut row_vec = vec![];
|
|
||||||
for (k, v) in &**val {
|
|
||||||
match v {
|
|
||||||
Value::List { ref vals, .. } => {
|
|
||||||
for v_item in vals {
|
|
||||||
row_vec.push((
|
|
||||||
k.clone(),
|
|
||||||
v_item.coerce_string().map_err(|_| {
|
|
||||||
ShellError::UnsupportedInput {
|
|
||||||
msg: "Expected a record with list of string values"
|
|
||||||
.to_string(),
|
|
||||||
input: "value originates from here".into(),
|
|
||||||
msg_span: head,
|
|
||||||
input_span: span,
|
|
||||||
}
|
|
||||||
})?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => row_vec.push((
|
|
||||||
k.clone(),
|
|
||||||
v.coerce_string()
|
|
||||||
.map_err(|_| ShellError::UnsupportedInput {
|
|
||||||
msg:
|
|
||||||
"Expected a record with string or list of string values"
|
|
||||||
.to_string(),
|
|
||||||
input: "value originates from here".into(),
|
|
||||||
msg_span: head,
|
|
||||||
input_span: span,
|
|
||||||
})?,
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
serde_urlencoded::to_string(row_vec).map_err(|_| ShellError::CantConvert {
|
|
||||||
to_type: "URL".into(),
|
|
||||||
from_type: value.get_type().to_string(),
|
|
||||||
span: head,
|
|
||||||
help: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// Propagate existing errors
|
// Propagate existing errors
|
||||||
Value::Error { error, .. } => Err(*error),
|
Value::Error { error, .. } => Err(*error),
|
||||||
other => Err(ShellError::UnsupportedInput {
|
other => Err(ShellError::UnsupportedInput {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
use super::query::record_to_query_string;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
|
@ -27,7 +29,7 @@ impl Command for SubCommand {
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Outputs a url representing the contents of this record",
|
description: "Outputs a url representing the contents of this record, `params` and `query` fields must be equivalent",
|
||||||
example: r#"{
|
example: r#"{
|
||||||
"scheme": "http",
|
"scheme": "http",
|
||||||
"username": "",
|
"username": "",
|
||||||
|
@ -47,6 +49,21 @@ impl Command for SubCommand {
|
||||||
"http://www.pixiv.net/member_illust.php?mode=medium&illust_id=99260204",
|
"http://www.pixiv.net/member_illust.php?mode=medium&illust_id=99260204",
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Outputs a url representing the contents of this record, \"exploding\" the list in `params` into multiple parameters",
|
||||||
|
example: r#"{
|
||||||
|
"scheme": "http",
|
||||||
|
"username": "user",
|
||||||
|
"password": "pwd",
|
||||||
|
"host": "www.pixiv.net",
|
||||||
|
"port": "1234",
|
||||||
|
"params": {a: ["one", "two"], b: "three"},
|
||||||
|
"fragment": ""
|
||||||
|
} | url join"#,
|
||||||
|
result: Some(Value::test_string(
|
||||||
|
"http://user:pwd@www.pixiv.net:1234?a=one&a=two&b=three",
|
||||||
|
)),
|
||||||
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Outputs a url representing the contents of this record",
|
description: "Outputs a url representing the contents of this record",
|
||||||
example: r#"{
|
example: r#"{
|
||||||
|
@ -178,16 +195,8 @@ impl UrlComponents {
|
||||||
|
|
||||||
if key == "params" {
|
if key == "params" {
|
||||||
return match value {
|
return match value {
|
||||||
Value::Record { val, .. } => {
|
Value::Record { ref val, .. } => {
|
||||||
let mut qs = val
|
let mut qs = record_to_query_string(val, value_span, span)?;
|
||||||
.into_owned()
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| match v.coerce_into_string() {
|
|
||||||
Ok(val) => Ok(format!("{k}={val}")),
|
|
||||||
Err(err) => Err(err),
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<String>, ShellError>>()?
|
|
||||||
.join("&");
|
|
||||||
|
|
||||||
qs = if !qs.trim().is_empty() {
|
qs = if !qs.trim().is_empty() {
|
||||||
format!("?{qs}")
|
format!("?{qs}")
|
||||||
|
|
|
@ -3,6 +3,7 @@ mod decode;
|
||||||
mod encode;
|
mod encode;
|
||||||
mod join;
|
mod join;
|
||||||
mod parse;
|
mod parse;
|
||||||
|
mod query;
|
||||||
mod url_;
|
mod url_;
|
||||||
|
|
||||||
pub use self::parse::SubCommand as UrlParse;
|
pub use self::parse::SubCommand as UrlParse;
|
||||||
|
|
44
crates/nu-command/src/network/url/query.rs
Normal file
44
crates/nu-command/src/network/url/query.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
use nu_protocol::{Record, ShellError, Span, Type, Value};
|
||||||
|
|
||||||
|
pub fn record_to_query_string(
|
||||||
|
record: &Record,
|
||||||
|
span: Span,
|
||||||
|
head: Span,
|
||||||
|
) -> Result<String, ShellError> {
|
||||||
|
let mut row_vec = vec![];
|
||||||
|
for (k, v) in record {
|
||||||
|
match v {
|
||||||
|
Value::List { ref vals, .. } => {
|
||||||
|
for v_item in vals {
|
||||||
|
row_vec.push((
|
||||||
|
k.as_str(),
|
||||||
|
v_item
|
||||||
|
.coerce_str()
|
||||||
|
.map_err(|_| ShellError::UnsupportedInput {
|
||||||
|
msg: "Expected a record with list of string values".to_string(),
|
||||||
|
input: "value originates from here".into(),
|
||||||
|
msg_span: head,
|
||||||
|
input_span: span,
|
||||||
|
})?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => row_vec.push((
|
||||||
|
k.as_str(),
|
||||||
|
v.coerce_str().map_err(|_| ShellError::UnsupportedInput {
|
||||||
|
msg: "Expected a record with string or list of string values".to_string(),
|
||||||
|
input: "value originates from here".into(),
|
||||||
|
msg_span: head,
|
||||||
|
input_span: span,
|
||||||
|
})?,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serde_urlencoded::to_string(row_vec).map_err(|_| ShellError::CantConvert {
|
||||||
|
to_type: "URL".into(),
|
||||||
|
from_type: Type::record().to_string(),
|
||||||
|
span: head,
|
||||||
|
help: None,
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue