crate::ix!();
pub async fn fix_nested_tables(
root_item: &mut TeItem,
lock_versions: &LockVersionMap,
root_cargo_toml: &dyn GetVersionOfLocalDep,
) -> Result<(), CargoTomlError> {
trace!("fix_nested_tables: start BFS over nested tables");
let mut queue: std::collections::VecDeque<Vec<String>> = std::collections::VecDeque::new();
queue.push_back(vec![]);
while let Some(path) = queue.pop_front() {
let maybe_item = get_item_mut_by_path(root_item, &path);
let curr_item = match maybe_item {
Some(i) => i,
None => {
continue; }
};
if let TeItem::Table(tbl) = curr_item {
let subkeys: Vec<String> = tbl.iter().map(|(k, _)| k.to_string()).collect();
for subkey in &subkeys {
if is_dependencies_key(subkey) {
if let Some(deps_item) = tbl.get_mut(subkey) {
if let TeItem::Table(dep_table) = deps_item {
pin_wildcard_dependencies_in_table(
dep_table,
lock_versions,
root_cargo_toml,
)
.await?;
}
}
}
}
for subkey in &subkeys {
let mut child_path = path.clone();
child_path.push(subkey.clone());
queue.push_back(child_path);
}
}
}
trace!("fix_nested_tables: end BFS");
Ok(())
}
fn get_item_mut_by_path<'a>(
root: &'a mut TeItem,
path: &[String],
) -> Option<&'a mut TeItem> {
let current: *mut TeItem = root as *mut TeItem;
if path.is_empty() {
return Some(root);
}
let mut curr_item: Option<&mut TeItem> = Some(root);
for (i, key) in path.iter().enumerate() {
let item = curr_item?;
match item {
TeItem::Table(tbl) => {
let sub = tbl.get_mut(key);
if sub.is_none() {
return None;
}
curr_item = sub;
}
_ => {
return None;
}
}
}
curr_item
}
#[cfg(test)]
mod test_fix_nested_tables {
use super::*;
use tracing::{info, debug, trace};
#[traced_test]
async fn pins_nested_dependencies() {
info!("Starting test_fix_nested_tables::pins_nested_dependencies");
let mut doc = TeItem::Table(TeTable::default());
if let TeItem::Table(tbl) = &mut doc {
let mut nested = TeTable::default();
nested.insert("dependencies", TeItem::Table(TeTable::default()));
if let Some(TeItem::Table(dep_tbl)) = nested.get_mut("dependencies") {
dep_tbl.insert("nesteddep", TeItem::Value(TeValue::from("*")));
}
tbl.insert("some_subtable", TeItem::Table(nested));
}
let mut lock_map = BTreeMap::new();
lock_map.insert(
"nesteddep".to_string(),
vec![SemverVersion::parse("2.0.0").unwrap()]
.into_iter()
.collect(),
);
struct MockGetVersion;
#[async_trait]
impl GetVersionOfLocalDep for MockGetVersion {
async fn version_of_local_dep(&self, _dep_name: &str, _dep_path: &str) -> Option<String> {
None
}
}
fix_nested_tables(&mut doc, &lock_map, &MockGetVersion).await.unwrap();
if let TeItem::Table(tbl) = &doc {
if let Some(TeItem::Table(sub)) = tbl.get("some_subtable") {
if let Some(TeItem::Table(deps)) = sub.get("dependencies") {
let pinned = deps.get("nesteddep").unwrap().as_str().unwrap();
assert_eq!(pinned, "2.0.0");
} else {
panic!("missing dependencies table");
}
} else {
panic!("missing some_subtable");
}
} else {
panic!("root is not a table");
}
debug!("test_fix_nested_tables::pins_nested_dependencies passed");
}
}