mirror of
https://github.com/nushell/nushell.git
synced 2025-08-10 02:48:00 +02:00
Return error instead of serializing closures as nil/null for msgpack and json, add --serialize for msgpack(z)
This commit is contained in:
@ -57,7 +57,7 @@ impl Command for ToJson {
|
|||||||
// allow ranges to expand and turn into array
|
// allow ranges to expand and turn into array
|
||||||
let input = input.try_expand_range()?;
|
let input = input.try_expand_range()?;
|
||||||
let value = input.into_value(span)?;
|
let value = input.into_value(span)?;
|
||||||
let json_value = value_to_json_value(engine_state, &value, serialize_types)?;
|
let json_value = value_to_json_value(engine_state, &value, span, serialize_types)?;
|
||||||
|
|
||||||
let json_result = if raw {
|
let json_result = if raw {
|
||||||
nu_json::to_string_raw(&json_value)
|
nu_json::to_string_raw(&json_value)
|
||||||
@ -78,16 +78,12 @@ impl Command for ToJson {
|
|||||||
};
|
};
|
||||||
Ok(PipelineData::Value(res, Some(metadata)))
|
Ok(PipelineData::Value(res, Some(metadata)))
|
||||||
}
|
}
|
||||||
_ => Ok(Value::error(
|
_ => Err(ShellError::CantConvert {
|
||||||
ShellError::CantConvert {
|
to_type: "JSON".into(),
|
||||||
to_type: "JSON".into(),
|
from_type: value.get_type().to_string(),
|
||||||
from_type: value.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
help: None,
|
|
||||||
},
|
|
||||||
span,
|
span,
|
||||||
)
|
help: None,
|
||||||
.into_pipeline_data()),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,6 +114,7 @@ impl Command for ToJson {
|
|||||||
pub fn value_to_json_value(
|
pub fn value_to_json_value(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
v: &Value,
|
v: &Value,
|
||||||
|
call_span: Span,
|
||||||
serialize_types: bool,
|
serialize_types: bool,
|
||||||
) -> Result<nu_json::Value, ShellError> {
|
) -> Result<nu_json::Value, ShellError> {
|
||||||
let span = v.span();
|
let span = v.span();
|
||||||
@ -142,7 +139,7 @@ pub fn value_to_json_value(
|
|||||||
),
|
),
|
||||||
|
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
nu_json::Value::Array(json_list(engine_state, vals, serialize_types)?)
|
nu_json::Value::Array(json_list(engine_state, vals, call_span, serialize_types)?)
|
||||||
}
|
}
|
||||||
Value::Error { error, .. } => return Err(*error.clone()),
|
Value::Error { error, .. } => return Err(*error.clone()),
|
||||||
Value::Closure { val, .. } => {
|
Value::Closure { val, .. } => {
|
||||||
@ -153,13 +150,23 @@ pub fn value_to_json_value(
|
|||||||
let contents_string = String::from_utf8_lossy(contents_bytes);
|
let contents_string = String::from_utf8_lossy(contents_bytes);
|
||||||
nu_json::Value::String(contents_string.to_string())
|
nu_json::Value::String(contents_string.to_string())
|
||||||
} else {
|
} else {
|
||||||
nu_json::Value::String(format!(
|
return Err(ShellError::CantConvert {
|
||||||
"unable to retrieve block contents for json block_id {}",
|
to_type: "string".into(),
|
||||||
val.block_id.get()
|
from_type: "closure".into(),
|
||||||
))
|
span,
|
||||||
|
help: Some(format!(
|
||||||
|
"unable to retrieve block contents for closure with id {}",
|
||||||
|
val.block_id.get()
|
||||||
|
)),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nu_json::Value::Null
|
return Err(ShellError::UnsupportedInput {
|
||||||
|
msg: "closures are currently not deserializable (use --serialize to serialize as a string)".into(),
|
||||||
|
input: "value originates from here".into(),
|
||||||
|
msg_span: call_span,
|
||||||
|
input_span: span,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Range { .. } => nu_json::Value::Null,
|
Value::Range { .. } => nu_json::Value::Null,
|
||||||
@ -171,14 +178,14 @@ pub fn value_to_json_value(
|
|||||||
for (k, v) in &**val {
|
for (k, v) in &**val {
|
||||||
m.insert(
|
m.insert(
|
||||||
k.clone(),
|
k.clone(),
|
||||||
value_to_json_value(engine_state, v, serialize_types)?,
|
value_to_json_value(engine_state, v, call_span, serialize_types)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
nu_json::Value::Object(m)
|
nu_json::Value::Object(m)
|
||||||
}
|
}
|
||||||
Value::Custom { val, .. } => {
|
Value::Custom { val, .. } => {
|
||||||
let collected = val.to_base_value(span)?;
|
let collected = val.to_base_value(span)?;
|
||||||
value_to_json_value(engine_state, &collected, serialize_types)?
|
value_to_json_value(engine_state, &collected, call_span, serialize_types)?
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -186,12 +193,18 @@ pub fn value_to_json_value(
|
|||||||
fn json_list(
|
fn json_list(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
input: &[Value],
|
input: &[Value],
|
||||||
|
call_span: Span,
|
||||||
serialize_types: bool,
|
serialize_types: bool,
|
||||||
) -> Result<Vec<nu_json::Value>, ShellError> {
|
) -> Result<Vec<nu_json::Value>, ShellError> {
|
||||||
let mut out = vec![];
|
let mut out = vec![];
|
||||||
|
|
||||||
for value in input {
|
for value in input {
|
||||||
out.push(value_to_json_value(engine_state, value, serialize_types)?);
|
out.push(value_to_json_value(
|
||||||
|
engine_state,
|
||||||
|
value,
|
||||||
|
call_span,
|
||||||
|
serialize_types,
|
||||||
|
)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(out)
|
Ok(out)
|
||||||
|
@ -22,6 +22,11 @@ impl Command for ToMsgpack {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.input_output_type(Type::Any, Type::Binary)
|
.input_output_type(Type::Any, Type::Binary)
|
||||||
|
.switch(
|
||||||
|
"serialize",
|
||||||
|
"serialize nushell types that cannot be deserialized",
|
||||||
|
Some('s'),
|
||||||
|
)
|
||||||
.category(Category::Formats)
|
.category(Category::Formats)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,8 +74,8 @@ MessagePack: https://msgpack.org/
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
_stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
@ -83,7 +88,16 @@ MessagePack: https://msgpack.org/
|
|||||||
let value = input.into_value(value_span)?;
|
let value = input.into_value(value_span)?;
|
||||||
let mut out = vec![];
|
let mut out = vec![];
|
||||||
|
|
||||||
write_value(&mut out, &value, 0)?;
|
let serialize_types = call.has_flag(engine_state, stack, "serialize")?;
|
||||||
|
|
||||||
|
write_value(
|
||||||
|
&mut out,
|
||||||
|
&value,
|
||||||
|
0,
|
||||||
|
engine_state,
|
||||||
|
call.head,
|
||||||
|
serialize_types,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(Value::binary(out, call.head).into_pipeline_data_with_metadata(Some(metadata)))
|
Ok(Value::binary(out, call.head).into_pipeline_data_with_metadata(Some(metadata)))
|
||||||
}
|
}
|
||||||
@ -148,6 +162,9 @@ pub(crate) fn write_value(
|
|||||||
out: &mut impl io::Write,
|
out: &mut impl io::Write,
|
||||||
value: &Value,
|
value: &Value,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
call_span: Span,
|
||||||
|
serialize_types: bool,
|
||||||
) -> Result<(), WriteError> {
|
) -> Result<(), WriteError> {
|
||||||
use mp::ValueWriteError::InvalidMarkerWrite;
|
use mp::ValueWriteError::InvalidMarkerWrite;
|
||||||
let span = value.span();
|
let span = value.span();
|
||||||
@ -196,6 +213,9 @@ pub(crate) fn write_value(
|
|||||||
out,
|
out,
|
||||||
&Value::list(val.into_range_iter(span, Signals::empty()).collect(), span),
|
&Value::list(val.into_range_iter(span, Signals::empty()).collect(), span),
|
||||||
depth,
|
depth,
|
||||||
|
engine_state,
|
||||||
|
call_span,
|
||||||
|
serialize_types,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Value::String { val, .. } => {
|
Value::String { val, .. } => {
|
||||||
@ -208,13 +228,20 @@ pub(crate) fn write_value(
|
|||||||
mp::write_map_len(out, convert(val.len(), span)?).err_span(span)?;
|
mp::write_map_len(out, convert(val.len(), span)?).err_span(span)?;
|
||||||
for (k, v) in val.iter() {
|
for (k, v) in val.iter() {
|
||||||
mp::write_str(out, k).err_span(span)?;
|
mp::write_str(out, k).err_span(span)?;
|
||||||
write_value(out, v, depth + 1)?;
|
write_value(out, v, depth + 1, engine_state, call_span, serialize_types)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
mp::write_array_len(out, convert(vals.len(), span)?).err_span(span)?;
|
mp::write_array_len(out, convert(vals.len(), span)?).err_span(span)?;
|
||||||
for val in vals {
|
for val in vals {
|
||||||
write_value(out, val, depth + 1)?;
|
write_value(
|
||||||
|
out,
|
||||||
|
val,
|
||||||
|
depth + 1,
|
||||||
|
engine_state,
|
||||||
|
call_span,
|
||||||
|
serialize_types,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Nothing { .. } => {
|
Value::Nothing { .. } => {
|
||||||
@ -222,11 +249,32 @@ pub(crate) fn write_value(
|
|||||||
.map_err(InvalidMarkerWrite)
|
.map_err(InvalidMarkerWrite)
|
||||||
.err_span(span)?;
|
.err_span(span)?;
|
||||||
}
|
}
|
||||||
Value::Closure { .. } => {
|
Value::Closure { val, .. } => {
|
||||||
// Closures can't be converted
|
if serialize_types {
|
||||||
mp::write_nil(out)
|
let block = engine_state.get_block(val.block_id);
|
||||||
.map_err(InvalidMarkerWrite)
|
let closure_string: &str = if let Some(span) = block.span {
|
||||||
.err_span(span)?;
|
let contents_bytes = engine_state.get_span_contents(span);
|
||||||
|
&String::from_utf8_lossy(contents_bytes)
|
||||||
|
} else {
|
||||||
|
return Err(WriteError::Shell(Box::new(ShellError::CantConvert {
|
||||||
|
to_type: "string".into(),
|
||||||
|
from_type: "closure".into(),
|
||||||
|
span,
|
||||||
|
help: Some(format!(
|
||||||
|
"unable to retrieve block contents for closure with id {}",
|
||||||
|
val.block_id.get()
|
||||||
|
)),
|
||||||
|
})));
|
||||||
|
};
|
||||||
|
mp::write_str(out, closure_string).err_span(span)?;
|
||||||
|
} else {
|
||||||
|
return Err(WriteError::Shell(Box::new(ShellError::UnsupportedInput {
|
||||||
|
msg: "closures are currently not deserializable (use --serialize to serialize as a string)".into(),
|
||||||
|
input: "value originates from here".into(),
|
||||||
|
msg_span: call_span,
|
||||||
|
input_span: span,
|
||||||
|
})));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Value::Error { error, .. } => {
|
Value::Error { error, .. } => {
|
||||||
return Err(WriteError::Shell(error.clone()));
|
return Err(WriteError::Shell(error.clone()));
|
||||||
@ -249,7 +297,14 @@ pub(crate) fn write_value(
|
|||||||
mp::write_bin(out, val).err_span(span)?;
|
mp::write_bin(out, val).err_span(span)?;
|
||||||
}
|
}
|
||||||
Value::Custom { val, .. } => {
|
Value::Custom { val, .. } => {
|
||||||
write_value(out, &val.to_base_value(span)?, depth)?;
|
write_value(
|
||||||
|
out,
|
||||||
|
&val.to_base_value(span)?,
|
||||||
|
depth,
|
||||||
|
engine_state,
|
||||||
|
call_span,
|
||||||
|
serialize_types,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -32,6 +32,11 @@ impl Command for ToMsgpackz {
|
|||||||
"Window size for brotli compression (default 20)",
|
"Window size for brotli compression (default 20)",
|
||||||
Some('w'),
|
Some('w'),
|
||||||
)
|
)
|
||||||
|
.switch(
|
||||||
|
"serialize",
|
||||||
|
"serialize nushell types that cannot be deserialized",
|
||||||
|
Some('s'),
|
||||||
|
)
|
||||||
.category(Category::Formats)
|
.category(Category::Formats)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +74,7 @@ impl Command for ToMsgpackz {
|
|||||||
.get_flag(engine_state, stack, "window-size")?
|
.get_flag(engine_state, stack, "window-size")?
|
||||||
.map(to_u32)
|
.map(to_u32)
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
let serialize_types = call.has_flag(engine_state, stack, "serialize")?;
|
||||||
|
|
||||||
let value_span = input.span().unwrap_or(call.head);
|
let value_span = input.span().unwrap_or(call.head);
|
||||||
let value = input.into_value(value_span)?;
|
let value = input.into_value(value_span)?;
|
||||||
@ -80,7 +86,14 @@ impl Command for ToMsgpackz {
|
|||||||
window_size.map(|w| w.item).unwrap_or(DEFAULT_WINDOW_SIZE),
|
window_size.map(|w| w.item).unwrap_or(DEFAULT_WINDOW_SIZE),
|
||||||
);
|
);
|
||||||
|
|
||||||
write_value(&mut out, &value, 0)?;
|
write_value(
|
||||||
|
&mut out,
|
||||||
|
&value,
|
||||||
|
0,
|
||||||
|
engine_state,
|
||||||
|
call.head,
|
||||||
|
serialize_types,
|
||||||
|
)?;
|
||||||
out.flush()
|
out.flush()
|
||||||
.map_err(|err| IoError::new(err.kind(), call.head, None))?;
|
.map_err(|err| IoError::new(err.kind(), call.head, None))?;
|
||||||
drop(out);
|
drop(out);
|
||||||
|
@ -69,16 +69,9 @@ impl Command for ToNuon {
|
|||||||
match nuon::to_nuon(engine_state, &value, style, Some(span), serialize_types) {
|
match nuon::to_nuon(engine_state, &value, style, Some(span), serialize_types) {
|
||||||
Ok(serde_nuon_string) => Ok(Value::string(serde_nuon_string, span)
|
Ok(serde_nuon_string) => Ok(Value::string(serde_nuon_string, span)
|
||||||
.into_pipeline_data_with_metadata(Some(metadata))),
|
.into_pipeline_data_with_metadata(Some(metadata))),
|
||||||
_ => Ok(Value::error(
|
Err(error) => {
|
||||||
ShellError::CantConvert {
|
Ok(Value::error(error, span).into_pipeline_data_with_metadata(Some(metadata)))
|
||||||
to_type: "NUON".into(),
|
}
|
||||||
from_type: value.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
help: None,
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
.into_pipeline_data_with_metadata(Some(metadata))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,7 +276,7 @@ fn send_json_request(
|
|||||||
) -> Result<Response, ShellErrorOrRequestError> {
|
) -> Result<Response, ShellErrorOrRequestError> {
|
||||||
match body {
|
match body {
|
||||||
Value::Int { .. } | Value::Float { .. } | Value::List { .. } | Value::Record { .. } => {
|
Value::Int { .. } | Value::Float { .. } | Value::List { .. } | Value::Record { .. } => {
|
||||||
let data = value_to_json_value(engine_state, &body, serialize_types)?;
|
let data = value_to_json_value(engine_state, &body, span, serialize_types)?;
|
||||||
send_cancellable_request(request_url, Box::new(|| req.send_json(data)), span, signals)
|
send_cancellable_request(request_url, Box::new(|| req.send_json(data)), span, signals)
|
||||||
}
|
}
|
||||||
// If the body type is string, assume it is string json content.
|
// If the body type is string, assume it is string json content.
|
||||||
|
@ -109,7 +109,7 @@ fn value_to_string(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(ShellError::UnsupportedInput {
|
Err(ShellError::UnsupportedInput {
|
||||||
msg: "closures are currently not nuon-compatible".into(),
|
msg: "closures are currently not deserializable (use --serialize to serialize as a string)".into(),
|
||||||
input: "value originates from here".into(),
|
input: "value originates from here".into(),
|
||||||
msg_span: span,
|
msg_span: span,
|
||||||
input_span: v.span(),
|
input_span: v.span(),
|
||||||
|
Reference in New Issue
Block a user