Make json require string and pass around metadata (#7010)

* Make json require string and pass around metadata

The json deserializer was accepting any inputs by coercing non-strings
into strings. As an example, if the input was `[1, 2]` the coercion
would turn into `[12]` and deserialize as a list containing number
twelve instead of a list of two numbers, one and two. This could lead
to silent data corruption.

Aside from that pipeline metadata wasn't passed aroud.

This commit fixes the type issue by adding a strict conversion
function that errors if the input type is not a string or external
stream. It then uses this function instead of the original
`collect_string()`. In addition, this function returns the pipeline
metadata so it can be passed along.

* Make other formats require string

The problem with json coercing non-string types to string was present in
all other text formats. This reuses the `collect_string_strict` function
to fix them.

* `IntoPipelineData` cleanup

The method `into_pipeline_data_with_metadata` can now be conveniently
used.
This commit is contained in:
Martin Habovštiak
2022-11-21 02:06:09 +01:00
committed by GitHub
parent d01ccd5a54
commit d9d6cea5a9
15 changed files with 104 additions and 95 deletions

View File

@ -211,6 +211,33 @@ impl PipelineData {
}
}
/// Retrieves string from pipline data.
///
/// As opposed to `collect_string` this raises error rather than converting non-string values.
/// The `span` will be used if `ListStream` is encountered since it doesn't carry a span.
pub fn collect_string_strict(
self,
span: Span,
) -> Result<(String, Option<PipelineMetadata>), ShellError> {
match self {
PipelineData::Value(Value::String { val, .. }, metadata) => Ok((val, metadata)),
PipelineData::Value(val, _) => {
Err(ShellError::TypeMismatch("string".into(), val.span()?))
}
PipelineData::ListStream(_, _) => Err(ShellError::TypeMismatch("string".into(), span)),
PipelineData::ExternalStream {
stdout: None,
metadata,
..
} => Ok((String::new(), metadata)),
PipelineData::ExternalStream {
stdout: Some(stdout),
metadata,
..
} => Ok((stdout.into_string()?.item, metadata)),
}
}
pub fn follow_cell_path(
self,
cell_path: &[PathMember],
@ -596,7 +623,10 @@ impl Iterator for PipelineIterator {
pub trait IntoPipelineData {
fn into_pipeline_data(self) -> PipelineData;
fn into_pipeline_data_with_metadata(self, metadata: PipelineMetadata) -> PipelineData;
fn into_pipeline_data_with_metadata(
self,
metadata: impl Into<Option<PipelineMetadata>>,
) -> PipelineData;
}
impl<V> IntoPipelineData for V
@ -606,8 +636,11 @@ where
fn into_pipeline_data(self) -> PipelineData {
PipelineData::Value(self.into(), None)
}
fn into_pipeline_data_with_metadata(self, metadata: PipelineMetadata) -> PipelineData {
PipelineData::Value(self.into(), Some(metadata))
fn into_pipeline_data_with_metadata(
self,
metadata: impl Into<Option<PipelineMetadata>>,
) -> PipelineData {
PipelineData::Value(self.into(), metadata.into())
}
}
@ -615,7 +648,7 @@ pub trait IntoInterruptiblePipelineData {
fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData;
fn into_pipeline_data_with_metadata(
self,
metadata: PipelineMetadata,
metadata: impl Into<Option<PipelineMetadata>>,
ctrlc: Option<Arc<AtomicBool>>,
) -> PipelineData;
}
@ -638,7 +671,7 @@ where
fn into_pipeline_data_with_metadata(
self,
metadata: PipelineMetadata,
metadata: impl Into<Option<PipelineMetadata>>,
ctrlc: Option<Arc<AtomicBool>>,
) -> PipelineData {
PipelineData::ListStream(
@ -646,7 +679,7 @@ where
stream: Box::new(self.into_iter().map(Into::into)),
ctrlc,
},
Some(metadata),
metadata.into(),
)
}
}