Skip to content

Commit

Permalink
feat: add /[] syntax to append to array at path
Browse files Browse the repository at this point in the history
  • Loading branch information
rmehri01 committed Jan 7, 2025
1 parent e68e7db commit cbe0300
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 10 deletions.
85 changes: 83 additions & 2 deletions src/adder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub fn handle_add(doc: &mut DocumentMut, op: AddOp) -> Result<()> {
.split('/')
.map(|s| s.to_string())
.collect::<Vec<String>>();
let dotted_path_vec =
let mut dotted_path_vec =
path.map(|p| p.split('/').map(|s| s.to_string()).collect::<Vec<String>>());
let field_value_json: JValue =
from_str(&value).context("parsing value field in add request")?;
Expand All @@ -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 => {
Expand Down Expand Up @@ -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::<DocumentMut>().unwrap();
Expand Down Expand Up @@ -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
"#
);
}
51 changes: 43 additions & 8 deletions src/adder/table_header_adder.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::{bail, Context, Result};
use anyhow::{anyhow, bail, Context, Result};
use toml_edit::{array, Item, Table, Value};

/*
Expand All @@ -19,13 +19,15 @@ pub fn add_value_with_table_header_and_dotted_path(
dotted_path: Option<Vec<String>>,
value: Item,
array_of_tables: bool,
append_array_at_path: bool,
) -> Result<()> {
match table_header_path.first() {
None => {
add_value_with_dotted_path(
table,
dotted_path.context("Missing 'path' value")?.as_slice(),
value,
append_array_at_path,
)?;
Ok(())
}
Expand All @@ -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(())
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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(()),
Expand All @@ -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(())
Expand All @@ -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")
Expand Down

0 comments on commit cbe0300

Please sign in to comment.