diff --git a/crates/nu-command/src/filters/insert.rs b/crates/nu-command/src/filters/insert.rs index 254c7b3d28..f9cd0b7779 100644 --- a/crates/nu-command/src/filters/insert.rs +++ b/crates/nu-command/src/filters/insert.rs @@ -1,9 +1,9 @@ use nu_engine::{eval_block, CallExt}; -use nu_protocol::ast::{Call, CellPath}; +use nu_protocol::ast::{Call, CellPath, PathMember}; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, FromValue, IntoPipelineData, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, + ShellError, Signature, Span, SyntaxShape, Value, }; #[derive(Clone)] @@ -121,6 +121,24 @@ fn insert( ctrlc, ) } else { + if let Some(PathMember::Int { val, .. }) = cell_path.members.get(0) { + let mut input = input.into_iter(); + let mut pre_elems = vec![]; + + for _ in 0..*val { + if let Some(v) = input.next() { + pre_elems.push(v); + } else { + pre_elems.push(Value::Nothing { span }) + } + } + + return Ok(pre_elems + .into_iter() + .chain(vec![replacement]) + .chain(input) + .into_pipeline_data(ctrlc)); + } input.map( move |mut input| { let replacement = replacement.clone(); diff --git a/crates/nu-command/src/filters/update.rs b/crates/nu-command/src/filters/update.rs index 8f9cd6760b..a287ecaf85 100644 --- a/crates/nu-command/src/filters/update.rs +++ b/crates/nu-command/src/filters/update.rs @@ -1,9 +1,9 @@ use nu_engine::{eval_block, CallExt}; -use nu_protocol::ast::{Call, CellPath}; +use nu_protocol::ast::{Call, CellPath, PathMember}; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, FromValue, IntoPipelineData, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, + ShellError, Signature, Span, SyntaxShape, Value, }; #[derive(Clone)] @@ -131,6 +131,27 @@ fn upsert( ctrlc, ) } else { + if let Some(PathMember::Int { val, span }) = cell_path.members.get(0) { + let mut input = input.into_iter(); + let mut pre_elems = vec![]; + + for idx in 0..*val { + if let Some(v) = input.next() { + pre_elems.push(v); + } else { + return Err(ShellError::AccessBeyondEnd(idx - 1, *span)); + } + } + + // Skip over the replaced value + let _ = input.next(); + + return Ok(pre_elems + .into_iter() + .chain(vec![replacement]) + .chain(input) + .into_pipeline_data(ctrlc)); + } input.map( move |mut input| { let replacement = replacement.clone(); diff --git a/crates/nu-command/tests/commands/insert.rs b/crates/nu-command/tests/commands/insert.rs index d935861263..38c3d429fe 100644 --- a/crates/nu-command/tests/commands/insert.rs +++ b/crates/nu-command/tests/commands/insert.rs @@ -26,3 +26,51 @@ fn insert_the_column_conflict() { assert!(actual.err.contains("column already exists")); } + +#[test] +fn insert_into_list() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [1, 2, 3] | insert 1 abc | to json -r + "# + )); + + assert_eq!(actual.out, r#"[1,"abc",2,3]"#); +} + +#[test] +fn insert_into_list_begin() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [1, 2, 3] | insert 0 abc | to json -r + "# + )); + + assert_eq!(actual.out, r#"["abc",1,2,3]"#); +} + +#[test] +fn insert_into_list_end() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [1, 2, 3] | insert 3 abc | to json -r + "# + )); + + assert_eq!(actual.out, r#"[1,2,3,"abc"]"#); +} + +#[test] +fn insert_past_end_list() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [1, 2, 3] | insert 5 abc | to json -r + "# + )); + + assert_eq!(actual.out, r#"[1,2,3,null,null,"abc"]"#); +} diff --git a/crates/nu-command/tests/commands/update.rs b/crates/nu-command/tests/commands/update.rs index f2dace3981..bbbffa6eaf 100644 --- a/crates/nu-command/tests/commands/update.rs +++ b/crates/nu-command/tests/commands/update.rs @@ -71,3 +71,27 @@ fn upsert_column_missing() { assert!(actual.err.contains("cannot find column")); } + +#[test] +fn update_list() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [1, 2, 3] | update 1 abc | to json -r + "# + )); + + assert_eq!(actual.out, r#"[1,"abc",3]"#); +} + +#[test] +fn update_past_end_list() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [1, 2, 3] | update 5 abc | to json -r + "# + )); + + assert!(actual.err.contains("too large")); +} diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs index 03fe42abe1..b35f8a683d 100644 --- a/crates/nu-protocol/src/shell_error.rs +++ b/crates/nu-protocol/src/shell_error.rs @@ -133,11 +133,11 @@ Either make sure {0} is a string, or add a 'to_string' entry for it in ENV_CONVE #[error("Row number too large (max: {0}).")] #[diagnostic(code(nu::shell::access_beyond_end), url(docsrs))] - AccessBeyondEnd(usize, #[label = "too large"] Span), + AccessBeyondEnd(usize, #[label = "index too large (max: {0})"] Span), #[error("Row number too large.")] #[diagnostic(code(nu::shell::access_beyond_end_of_stream), url(docsrs))] - AccessBeyondEndOfStream(#[label = "too large"] Span), + AccessBeyondEndOfStream(#[label = "index too large"] Span), #[error("Data cannot be accessed with a cell path")] #[diagnostic(code(nu::shell::incompatible_path_access), url(docsrs))]