From 50feca4d12c11f2c116691eb6f63be317e0cefeb Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Tue, 7 Jan 2025 10:26:31 -0800 Subject: [PATCH 1/4] style: run cargo clippy --fix --- src/adder.rs | 4 ++-- src/adder/table_header_adder.rs | 10 +++++----- src/field_finder.rs | 10 +++++----- src/main.rs | 4 ++-- src/traversal.rs | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/adder.rs b/src/adder.rs index 5e40339..634ab03 100644 --- a/src/adder.rs +++ b/src/adder.rs @@ -188,9 +188,9 @@ test = "yo" let op = AddOp { path: field, - table_header_path: table_header_path, + table_header_path, dotted_path: None, - value: value, + value, }; let $result = handle_add(&mut doc, op); $( diff --git a/src/adder/table_header_adder.rs b/src/adder/table_header_adder.rs index 4abbc13..d56ae5d 100644 --- a/src/adder/table_header_adder.rs +++ b/src/adder/table_header_adder.rs @@ -20,7 +20,7 @@ pub fn add_value_with_table_header_and_dotted_path( value: Item, array_of_tables: bool, ) -> Result<()> { - match table_header_path.get(0) { + match table_header_path.first() { None => { add_value_with_dotted_path( table, @@ -108,16 +108,16 @@ fn add_value_with_dotted_path( dotted_path: &[String], value: Item, ) -> Result<()> { - match dotted_path.get(0) { + match dotted_path.first() { None => Ok(()), Some(field) => match table.get_mut(field) { None | Some(Item::None) => { if dotted_path.len() > 1 { let mut inner_table = Table::new(); inner_table.set_dotted(true); - return add_value_with_dotted_path(&mut inner_table, &dotted_path[1..], value) + add_value_with_dotted_path(&mut inner_table, &dotted_path[1..], value) .map(|_| table.insert(field, Item::Table(inner_table))) - .map(|_| ()); + .map(|_| ()) } else { table.insert(field, value); Ok(()) @@ -126,7 +126,7 @@ fn add_value_with_dotted_path( Some(Item::Table(ref mut inner_table)) => { if dotted_path.len() > 1 { inner_table.set_dotted(true); - return add_value_with_dotted_path(inner_table, &dotted_path[1..], value); + add_value_with_dotted_path(inner_table, &dotted_path[1..], value) } else { table.insert(field, value); Ok(()) diff --git a/src/field_finder.rs b/src/field_finder.rs index 140cef5..f682635 100644 --- a/src/field_finder.rs +++ b/src/field_finder.rs @@ -34,7 +34,7 @@ fn descend_table<'a>( do_insert: DoInsert, last_field: &String, ) -> Result> { - let segment = match path.get(0) { + let segment = match path.first() { Some(segment) => segment, None => return Ok(TomlValue::Table(table)), }; @@ -103,7 +103,7 @@ fn descend_array_of_tables<'a>( do_insert: DoInsert, last_field: &String, ) -> Result> { - let segment = match path.get(0) { + let segment = match path.first() { Some(segment) => segment, None => return Ok(TomlValue::ArrayOfTables(array)), }; @@ -143,7 +143,7 @@ fn descend_inline_table<'a>( do_insert: DoInsert, last_field: &String, ) -> Result> { - let segment = match path.get(0) { + let segment = match path.first() { Some(segment) => segment, None => return Ok(TomlValue::InlineTable(inline_table)), }; @@ -165,7 +165,7 @@ fn descend_array<'a>( do_insert: DoInsert, last_field: &String, ) -> Result> { - let segment = match path.get(0) { + let segment = match path.first() { Some(segment) => segment, None => return Ok(TomlValue::Array(array)), }; @@ -205,7 +205,7 @@ bla = "bla" let mut doc = doc_string.parse::().unwrap(); let val = get_field( - &(vec!["foo".to_string()]), + &["foo".to_string()], &"bar".to_string(), DoInsert::Yes, &mut doc, diff --git a/src/main.rs b/src/main.rs index 53e052d..434490a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -106,7 +106,7 @@ fn do_edits( // we need to re-read the file each time since the user might manually edit the // file and so we need to make sure we have the most up to date version. - let dotreplit_contents = match fs::read_to_string(&dotreplit_filepath) { + let dotreplit_contents = match fs::read_to_string(dotreplit_filepath) { Ok(contents) => contents, Err(err) if err.kind() == io::ErrorKind::NotFound => "".to_string(), // if .replit doesn't exist start with an empty one Err(_) => return Err(anyhow!("error: reading file - {:?}", &dotreplit_filepath)), @@ -146,7 +146,7 @@ fn do_edits( // write the file back to disk if changed { - fs::write(&dotreplit_filepath, doc.to_string()) + fs::write(dotreplit_filepath, doc.to_string()) .with_context(|| format!("error: writing file: {:?}", &dotreplit_filepath))?; } Ok(("".to_string(), outputs)) diff --git a/src/traversal.rs b/src/traversal.rs index 13cd211..d7d14df 100644 --- a/src/traversal.rs +++ b/src/traversal.rs @@ -53,7 +53,7 @@ pub fn traverse<'a>( } match op { - TraverseOps::Get => result?.to_value().map(|v| Some(v)), + TraverseOps::Get => result?.to_value().map(Some), } } From 4419b13d2522b385d5042984a79b577193d4fdff Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Tue, 7 Jan 2025 10:55:34 -0800 Subject: [PATCH 2/4] style: use ? instead of map chain --- src/adder/table_header_adder.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/adder/table_header_adder.rs b/src/adder/table_header_adder.rs index d56ae5d..d88f7e0 100644 --- a/src/adder/table_header_adder.rs +++ b/src/adder/table_header_adder.rs @@ -115,9 +115,9 @@ fn add_value_with_dotted_path( if dotted_path.len() > 1 { let mut inner_table = Table::new(); inner_table.set_dotted(true); - add_value_with_dotted_path(&mut inner_table, &dotted_path[1..], value) - .map(|_| table.insert(field, Item::Table(inner_table))) - .map(|_| ()) + add_value_with_dotted_path(&mut inner_table, &dotted_path[1..], value)?; + table.insert(field, Item::Table(inner_table)); + Ok(()) } else { table.insert(field, value); Ok(()) From e68e7dbe81e911cc9dbb830bb7520585890bf33e Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Tue, 7 Jan 2025 11:00:53 -0800 Subject: [PATCH 3/4] style: use &str instead of &String since it's more flexible --- src/field_finder.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/field_finder.rs b/src/field_finder.rs index f682635..79e4479 100644 --- a/src/field_finder.rs +++ b/src/field_finder.rs @@ -19,7 +19,7 @@ pub enum DoInsert { pub fn get_field<'a>( path: &[String], - last_field: &String, + last_field: &str, do_insert: DoInsert, doc: &'a mut DocumentMut, ) -> Result> { @@ -32,7 +32,7 @@ fn descend_table<'a>( table: &'a mut Table, path: &[String], do_insert: DoInsert, - last_field: &String, + last_field: &str, ) -> Result> { let segment = match path.first() { Some(segment) => segment, @@ -67,7 +67,7 @@ fn descend_item<'a>( item: &'a mut Item, path: &[String], do_insert: DoInsert, - last_field: &String, + last_field: &str, ) -> Result> { match item { Item::Table(table) => descend_table(table, path, do_insert, last_field), @@ -81,7 +81,7 @@ fn descend_value<'a>( value: &'a mut Value, path: &[String], do_insert: DoInsert, - last_field: &String, + last_field: &str, ) -> Result> { match value { Value::Array(array) => descend_array(array, path, do_insert, last_field), @@ -101,7 +101,7 @@ fn descend_array_of_tables<'a>( array: &'a mut ArrayOfTables, path: &[String], do_insert: DoInsert, - last_field: &String, + last_field: &str, ) -> Result> { let segment = match path.first() { Some(segment) => segment, @@ -141,7 +141,7 @@ fn descend_inline_table<'a>( inline_table: &'a mut InlineTable, path: &[String], do_insert: DoInsert, - last_field: &String, + last_field: &str, ) -> Result> { let segment = match path.first() { Some(segment) => segment, @@ -163,7 +163,7 @@ fn descend_array<'a>( array: &'a mut Array, path: &[String], do_insert: DoInsert, - last_field: &String, + last_field: &str, ) -> Result> { let segment = match path.first() { Some(segment) => segment, @@ -206,7 +206,7 @@ bla = "bla" let mut doc = doc_string.parse::().unwrap(); let val = get_field( &["foo".to_string()], - &"bar".to_string(), + "bar", DoInsert::Yes, &mut doc, ) @@ -224,7 +224,7 @@ bla = "bla" let doc_string = r#"test = [ 1 ]"#; let mut doc = doc_string.parse::().unwrap(); let fields = ["test".to_string()]; - let val = get_field(&(fields), &"1".to_string(), DoInsert::Yes, &mut doc).unwrap(); + let val = get_field(&(fields), "1", DoInsert::Yes, &mut doc).unwrap(); if let TomlValue::Array(array) = val { assert_eq!(array.len(), 1); @@ -243,7 +243,7 @@ foo = "baz" "#; let mut doc = doc_string.parse::().unwrap(); let fields = ["test".to_string()]; - let val = get_field(&(fields), &"2".to_string(), DoInsert::Yes, &mut doc).unwrap(); + let val = get_field(&(fields), "2", DoInsert::Yes, &mut doc).unwrap(); if let TomlValue::ArrayOfTables(array) = val { assert_eq!(array.len(), 2); From cbe0300577e0eb536516b2f801ea9aace926ec50 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Tue, 7 Jan 2025 11:51:58 -0800 Subject: [PATCH 4/4] feat: add /[] syntax to append to array at path --- src/adder.rs | 85 ++++++++++++++++++++++++++++++++- src/adder/table_header_adder.rs | 51 ++++++++++++++++---- 2 files changed, 126 insertions(+), 10 deletions(-) diff --git a/src/adder.rs b/src/adder.rs index 634ab03..2c8bc73 100644 --- a/src/adder.rs +++ b/src/adder.rs @@ -19,7 +19,7 @@ pub fn handle_add(doc: &mut DocumentMut, op: AddOp) -> Result<()> { .split('/') .map(|s| s.to_string()) .collect::>(); - let dotted_path_vec = + let mut dotted_path_vec = path.map(|p| p.split('/').map(|s| s.to_string()).collect::>()); let field_value_json: JValue = from_str(&value).context("parsing value field in add request")?; @@ -35,12 +35,20 @@ pub fn handle_add(doc: &mut DocumentMut, op: AddOp) -> Result<()> { } else { false }; + let append_array_at_path = match &mut dotted_path_vec { + Some(path_vec) if path_vec.last().is_some_and(|key| key == "[]") => { + path_vec.pop(); + true + } + _ => false, + }; table_header_adder::add_value_with_table_header_and_dotted_path( doc, &table_header_path_vec, dotted_path_vec, field_value_toml, array_of_tables, + append_array_at_path, ) } None => { @@ -177,7 +185,16 @@ test = "yo" none = "all""#; macro_rules! meta_add_test { - ($name:ident, $table_header_path:expr, $field:expr, $value:expr, $contents:expr, $expected:expr, $result:ident, $($assertion:stmt)*) => { + ( + $name:ident, + $table_header_path:expr, + $field:expr, + $value:expr, + $contents:expr, + $expected:expr, + $result:ident, + $($assertion:stmt)* + ) => { #[test] fn $name() { let mut doc = $contents.parse::().unwrap(); @@ -674,4 +691,68 @@ key = "second" torchvision = [{ index = "pytorch-cpu", marker = "platform_system == 'Linux'" }] "# ); + + add_table_header_test!( + test_append_array_at_path_empty, + Some("tool/uv/sources"), + Some("torch/[]"), + r#" + {"index": "pytorch-cpu", "marker": "platform_system == 'Linux'"} + "#, + r#" + "#, + r#" +[tool.uv.sources] +torch = [{ index = "pytorch-cpu", marker = "platform_system == 'Linux'" }] + "# + ); + + add_table_header_test!( + test_append_array_at_path_empty_dotted_path, + Some("tool/uv/sources"), + Some("torch/test/[]"), + r#" + {"index": "pytorch-cpu", "marker": "platform_system == 'Linux'"} + "#, + r#" + "#, + r#" +[tool.uv.sources] +torch.test = [{ index = "pytorch-cpu", marker = "platform_system == 'Linux'" }] + "# + ); + + add_table_header_test!( + test_append_array_at_path_existing, + Some("tool/uv/sources"), + Some("torch/[]"), + r#" + {"index": "pytorch-cpu", "marker": "platform_system == 'Linux'"} + "#, + r#" +[tool.uv.sources] +torch = [{ index = "foo", marker = "platform_system == 'Windows'" }] + "#, + r#" +[tool.uv.sources] +torch = [{ index = "foo", marker = "platform_system == 'Windows'" }, { index = "pytorch-cpu", marker = "platform_system == 'Linux'" }] + "# + ); + + add_table_header_error_test!( + test_append_array_at_path_existing_non_array_at_path, + Some("tool/uv/sources"), + Some("torch/[]"), + r#" + {"index": "pytorch-cpu", "marker": "platform_system == 'Linux'"} + "#, + r#" +[tool.uv.sources] +torch = 1 + "#, + r#" +[tool.uv.sources] +torch = 1 + "# + ); } diff --git a/src/adder/table_header_adder.rs b/src/adder/table_header_adder.rs index d88f7e0..a1c4dc1 100644 --- a/src/adder/table_header_adder.rs +++ b/src/adder/table_header_adder.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use toml_edit::{array, Item, Table, Value}; /* @@ -19,6 +19,7 @@ pub fn add_value_with_table_header_and_dotted_path( dotted_path: Option>, value: Item, array_of_tables: bool, + append_array_at_path: bool, ) -> Result<()> { match table_header_path.first() { None => { @@ -26,6 +27,7 @@ pub fn add_value_with_table_header_and_dotted_path( table, dotted_path.context("Missing 'path' value")?.as_slice(), value, + append_array_at_path, )?; Ok(()) } @@ -38,6 +40,7 @@ pub fn add_value_with_table_header_and_dotted_path( dotted_path, value, array_of_tables, + append_array_at_path, )?; Ok(()) } @@ -51,6 +54,7 @@ pub fn add_value_with_table_header_and_dotted_path( dotted_path, value, array_of_tables, + append_array_at_path, )?; table.insert(field, Item::Table(inner_table)); } else { @@ -107,6 +111,7 @@ fn add_value_with_dotted_path( table: &mut Table, dotted_path: &[String], value: Item, + append_array_at_path: bool, ) -> Result<()> { match dotted_path.first() { None => Ok(()), @@ -115,9 +120,23 @@ fn add_value_with_dotted_path( if dotted_path.len() > 1 { let mut inner_table = Table::new(); inner_table.set_dotted(true); - add_value_with_dotted_path(&mut inner_table, &dotted_path[1..], value)?; + add_value_with_dotted_path( + &mut inner_table, + &dotted_path[1..], + value, + append_array_at_path, + )?; table.insert(field, Item::Table(inner_table)); Ok(()) + } else if append_array_at_path { + let mut arr = toml_edit::Array::new(); + arr.push( + value + .into_value() + .map_err(|_| anyhow!("Cannot append non-value item to array"))?, + ); + table.insert(field, Item::Value(Value::Array(arr))); + Ok(()) } else { table.insert(field, value); Ok(()) @@ -126,19 +145,35 @@ fn add_value_with_dotted_path( Some(Item::Table(ref mut inner_table)) => { if dotted_path.len() > 1 { inner_table.set_dotted(true); - add_value_with_dotted_path(inner_table, &dotted_path[1..], value) + add_value_with_dotted_path( + inner_table, + &dotted_path[1..], + value, + append_array_at_path, + ) } else { table.insert(field, value); Ok(()) } } - Some(Item::Value(_)) => { - if dotted_path.len() == 1 { - table.insert(field, value); - Ok(()) - } else { + Some(item @ Item::Value(_)) => { + if dotted_path.len() != 1 { bail!("Cannot overwrite a non-table with a table") } + + if append_array_at_path { + let arr = item + .as_array_mut() + .context(format!("Cannot append non-array field '{field}'"))?; + arr.push( + value + .into_value() + .map_err(|_| anyhow!("Cannot append non-value item to array"))?, + ); + } else { + table.insert(field, value); + } + Ok(()) } Some(Item::ArrayOfTables(_)) => { bail!("Cannot add key to a array of tables")