mirror of
https://github.com/nushell/nushell
synced 2024-12-25 12:33:17 +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 super::query::record_to_query_string;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
|
||||
|
@ -38,12 +40,12 @@ impl Command for SubCommand {
|
|||
result: Some(Value::test_string("foo=1&bar=2")),
|
||||
},
|
||||
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"#,
|
||||
result: Some(Value::test_string("a=AT%26T&b=AT+T")),
|
||||
},
|
||||
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"#,
|
||||
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| {
|
||||
let span = value.span();
|
||||
match value {
|
||||
Value::Record { ref val, .. } => {
|
||||
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,
|
||||
})
|
||||
}
|
||||
Value::Record { ref val, .. } => record_to_query_string(val, span, head),
|
||||
// Propagate existing errors
|
||||
Value::Error { error, .. } => Err(*error),
|
||||
other => Err(ShellError::UnsupportedInput {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use nu_engine::command_prelude::*;
|
||||
|
||||
use super::query::record_to_query_string;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
|
||||
|
@ -27,7 +29,7 @@ impl Command for SubCommand {
|
|||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
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#"{
|
||||
"scheme": "http",
|
||||
"username": "",
|
||||
|
@ -47,6 +49,21 @@ impl Command for SubCommand {
|
|||
"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 {
|
||||
description: "Outputs a url representing the contents of this record",
|
||||
example: r#"{
|
||||
|
@ -178,16 +195,8 @@ impl UrlComponents {
|
|||
|
||||
if key == "params" {
|
||||
return match value {
|
||||
Value::Record { val, .. } => {
|
||||
let mut qs = val
|
||||
.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("&");
|
||||
Value::Record { ref val, .. } => {
|
||||
let mut qs = record_to_query_string(val, value_span, span)?;
|
||||
|
||||
qs = if !qs.trim().is_empty() {
|
||||
format!("?{qs}")
|
||||
|
|
|
@ -3,6 +3,7 @@ mod decode;
|
|||
mod encode;
|
||||
mod join;
|
||||
mod parse;
|
||||
mod query;
|
||||
mod url_;
|
||||
|
||||
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