Fix #15571 panic on write to source parquet file (#15601)

Fixes #15571 

# Description

Writing to a source `.parquet` (`polars open some_file.parquet | polars
save some_file.parquet`) file made the plugin panic, added a guard to
check the data_source path as per [this
comment](https://github.com/nushell/nushell/issues/15571#issuecomment-2812707161)

Example output now:
<img width="850" alt="Screenshot 2025-04-18 at 21 10 30"
src="https://github.com/user-attachments/assets/40a73cc7-6635-43dc-a423-19c7a0c8f59c"
/>

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

# Tests + Formatting
 - Add 1 test
 - clippy OK
 - cargo fmt OK

# After Submitting
No action required
This commit is contained in:
Julian Amarilla
2025-05-23 20:31:28 -03:00
committed by GitHub
parent f90035e084
commit 6bf955a5a5
110 changed files with 452 additions and 165 deletions

View File

@ -1,6 +1,7 @@
use super::util::{build_metadata_record, extend_record_with_metadata};
use nu_engine::command_prelude::*;
use nu_protocol::{
DataSource, PipelineMetadata,
PipelineMetadata,
ast::{Expr, Expression},
};
@ -56,56 +57,40 @@ impl Command for Metadata {
} => {
let origin = stack.get_var_with_origin(*var_id, *span)?;
Ok(
build_metadata_record(&origin, input.metadata().as_ref(), head)
.into_pipeline_data(),
Ok(build_metadata_record_value(
&origin,
input.metadata().as_ref(),
head,
)
.into_pipeline_data())
}
_ => {
let val: Value = call.req(engine_state, stack, 0)?;
Ok(build_metadata_record(&val, input.metadata().as_ref(), head)
.into_pipeline_data())
Ok(
build_metadata_record_value(&val, input.metadata().as_ref(), head)
.into_pipeline_data(),
)
}
}
} else {
let val: Value = call.req(engine_state, stack, 0)?;
Ok(build_metadata_record(&val, input.metadata().as_ref(), head)
.into_pipeline_data())
Ok(
build_metadata_record_value(&val, input.metadata().as_ref(), head)
.into_pipeline_data(),
)
}
}
Some(_) => {
let val: Value = call.req(engine_state, stack, 0)?;
Ok(build_metadata_record(&val, input.metadata().as_ref(), head)
.into_pipeline_data())
}
None => {
let mut record = Record::new();
if let Some(x) = input.metadata().as_ref() {
match x {
PipelineMetadata {
data_source: DataSource::Ls,
..
} => record.push("source", Value::string("ls", head)),
PipelineMetadata {
data_source: DataSource::HtmlThemes,
..
} => record.push("source", Value::string("into html --list", head)),
PipelineMetadata {
data_source: DataSource::FilePath(path),
..
} => record.push(
"source",
Value::string(path.to_string_lossy().to_string(), head),
),
_ => {}
}
if let Some(ref content_type) = x.content_type {
record.push("content_type", Value::string(content_type, head));
}
}
Ok(Value::record(record, head).into_pipeline_data())
Ok(
build_metadata_record_value(&val, input.metadata().as_ref(), head)
.into_pipeline_data(),
)
}
None => Ok(
Value::record(build_metadata_record(input.metadata().as_ref(), head), head)
.into_pipeline_data(),
),
}
}
@ -125,7 +110,11 @@ impl Command for Metadata {
}
}
fn build_metadata_record(arg: &Value, metadata: Option<&PipelineMetadata>, head: Span) -> Value {
fn build_metadata_record_value(
arg: &Value,
metadata: Option<&PipelineMetadata>,
head: Span,
) -> Value {
let mut record = Record::new();
let span = arg.span();
@ -140,31 +129,7 @@ fn build_metadata_record(arg: &Value, metadata: Option<&PipelineMetadata>, head:
),
);
if let Some(x) = metadata {
match x {
PipelineMetadata {
data_source: DataSource::Ls,
..
} => record.push("source", Value::string("ls", head)),
PipelineMetadata {
data_source: DataSource::HtmlThemes,
..
} => record.push("source", Value::string("into html --list", head)),
PipelineMetadata {
data_source: DataSource::FilePath(path),
..
} => record.push(
"source",
Value::string(path.to_string_lossy().to_string(), head),
),
_ => {}
}
if let Some(ref content_type) = x.content_type {
record.push("content_type", Value::string(content_type, head));
}
}
Value::record(record, head)
Value::record(extend_record_with_metadata(record, metadata, head), head)
}
#[cfg(test)]

View File

@ -1,9 +1,11 @@
use nu_engine::{command_prelude::*, get_eval_block_with_early_return};
use nu_protocol::{
DataSource, PipelineData, PipelineMetadata, ShellError, Signature, SyntaxShape, Type, Value,
PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
engine::{Call, Closure, Command, EngineState, Stack},
};
use super::util::build_metadata_record;
#[derive(Clone)]
pub struct MetadataAccess;
@ -40,7 +42,10 @@ impl Command for MetadataAccess {
// `ClosureEvalOnce` is not used as it uses `Stack::captures_to_stack` rather than
// `Stack::captures_to_stack_preserve_out_dest`. This command shouldn't collect streams
let mut callee_stack = caller_stack.captures_to_stack_preserve_out_dest(closure.captures);
let metadata_record = build_metadata_record(input.metadata().as_ref(), call.head);
let metadata_record = Value::record(
build_metadata_record(input.metadata().as_ref(), call.head),
call.head,
);
if let Some(var_id) = block.signature.get_positional(0).and_then(|var| var.var_id) {
callee_stack.add_var(var_id, metadata_record)
@ -64,36 +69,6 @@ impl Command for MetadataAccess {
}
}
fn build_metadata_record(metadata: Option<&PipelineMetadata>, head: Span) -> Value {
let mut record = Record::new();
if let Some(x) = metadata {
match x {
PipelineMetadata {
data_source: DataSource::Ls,
..
} => record.push("source", Value::string("ls", head)),
PipelineMetadata {
data_source: DataSource::HtmlThemes,
..
} => record.push("source", Value::string("into html --list", head)),
PipelineMetadata {
data_source: DataSource::FilePath(path),
..
} => record.push(
"source",
Value::string(path.to_string_lossy().to_string(), head),
),
_ => {}
}
if let Some(ref content_type) = x.content_type {
record.push("content_type", Value::string(content_type, head));
}
}
Value::record(record, head)
}
#[cfg(test)]
mod test {
use crate::ToJson;

View File

@ -9,6 +9,7 @@ mod metadata_access;
mod metadata_set;
mod profile;
mod timeit;
mod util;
mod view;
mod view_blocks;
mod view_files;

View File

@ -0,0 +1,34 @@
use nu_protocol::{DataSource, PipelineMetadata, Record, Span, Value};
pub fn extend_record_with_metadata(
mut record: Record,
metadata: Option<&PipelineMetadata>,
head: Span,
) -> Record {
if let Some(PipelineMetadata {
data_source,
content_type,
}) = metadata
{
match data_source {
DataSource::Ls => record.push("source", Value::string("ls", head)),
DataSource::HtmlThemes => {
record.push("source", Value::string("into html --list", head))
}
DataSource::FilePath(path) => record.push(
"source",
Value::string(path.to_string_lossy().to_string(), head),
),
DataSource::None => {}
}
if let Some(content_type) = content_type {
record.push("content_type", Value::string(content_type, head));
}
};
record
}
pub fn build_metadata_record(metadata: Option<&PipelineMetadata>, head: Span) -> Record {
extend_record_with_metadata(Record::new(), metadata, head)
}