use super::diff::{create_ir_node_tree_impl, reconcile_ir_node_impl, ReconcileCtx};
use super::item_bindings::replace_ir_node_item_bindings;
use super::Patch;
use crate::ir::{IRNode, NodeId};
use indexmap::IndexMap;
pub(crate) fn generate_item_key(
item: &serde_json::Value,
key_path: Option<&str>,
item_name: &str,
index: usize,
) -> String {
fn stringify(v: &serde_json::Value) -> Option<String> {
match v {
serde_json::Value::String(s) => Some(s.clone()),
serde_json::Value::Number(n) => Some(n.to_string()),
serde_json::Value::Bool(b) => Some(b.to_string()),
_ => None,
}
}
if let Some(kp) = key_path {
if let Some(s) = item.get(kp).and_then(stringify) {
return s;
}
}
if let Some(s) = item.get("id").and_then(stringify) {
return s;
}
format!("{}-{}", item_name, index)
}
pub(crate) fn reconcile_iterable_children(
ctx: &mut ReconcileCtx,
parent_id: NodeId,
items: &[serde_json::Value],
item_name: &str,
key_path: Option<&str>,
ir_templates: &[IRNode],
) {
let multi_template = ir_templates.len() > 1;
let old_children: Vec<NodeId> = ctx
.tree
.get(parent_id)
.map(|n| n.children.iter().copied().collect())
.unwrap_or_default();
let mut old_keyed: IndexMap<String, NodeId> = IndexMap::new();
let mut old_unkeyed: Vec<NodeId> = Vec::new();
for &child_id in &old_children {
match ctx.tree.get(child_id).and_then(|n| n.key.clone()) {
Some(key) => {
old_keyed.insert(key, child_id);
}
None => old_unkeyed.push(child_id),
}
}
if let Some(parent_node) = ctx.tree.get_mut(parent_id) {
parent_node.children.clear();
}
let mut new_child_ids: Vec<NodeId> = Vec::with_capacity(items.len() * ir_templates.len());
let mut new_child_keys: Vec<String> = Vec::with_capacity(new_child_ids.capacity());
let mut unkeyed_idx = 0;
for (item_index, item) in items.iter().enumerate() {
let item_key = generate_item_key(item, key_path, item_name, item_index);
for (template_idx, template) in ir_templates.iter().enumerate() {
let child_key = if multi_template {
format!("{}#{}", item_key, template_idx)
} else {
item_key.clone()
};
let child_id = if let Some(&old_id) = old_keyed.get(&child_key) {
let substituted =
replace_ir_node_item_bindings(template, item, item_index, item_name, &item_key);
if let Some(parent_node) = ctx.tree.get_mut(parent_id) {
parent_node.children.push_back(old_id);
}
reconcile_ir_node_impl(ctx, old_id, &substituted);
old_keyed.shift_remove(&child_key);
old_id
} else if let Some(&old_id) = old_unkeyed.get(unkeyed_idx) {
let substituted =
replace_ir_node_item_bindings(template, item, item_index, item_name, &item_key);
if let Some(parent_node) = ctx.tree.get_mut(parent_id) {
parent_node.children.push_back(old_id);
}
reconcile_ir_node_impl(ctx, old_id, &substituted);
unkeyed_idx += 1;
old_id
} else {
let substituted =
replace_ir_node_item_bindings(template, item, item_index, item_name, &item_key);
create_ir_node_tree_impl(ctx, &substituted, Some(parent_id), false)
};
new_child_ids.push(child_id);
new_child_keys.push(child_key);
}
}
for (id, key) in new_child_ids.iter().zip(new_child_keys.iter()) {
if let Some(node) = ctx.tree.get_mut(*id) {
node.key = Some(key.clone());
}
}
let old_positions: Vec<Option<usize>> = new_child_ids
.iter()
.map(|new_id| old_children.iter().position(|old_id| old_id == new_id))
.collect();
let lis = longest_increasing_subsequence(&old_positions);
for (new_idx, &child_id) in new_child_ids.iter().enumerate() {
if old_positions[new_idx].is_none() {
continue;
}
if !lis.contains(&new_idx) {
let before_id = new_child_ids.get(new_idx + 1).copied();
ctx.patches
.push(Patch::move_node(parent_id, child_id, before_id));
}
}
for &old_id in old_keyed.values() {
ctx.dependencies.remove_node(old_id);
ctx.tree.remove(old_id);
ctx.patches.push(Patch::remove(old_id));
}
for &old_id in &old_unkeyed[unkeyed_idx..] {
ctx.dependencies.remove_node(old_id);
ctx.tree.remove(old_id);
ctx.patches.push(Patch::remove(old_id));
}
}
fn longest_increasing_subsequence(positions: &[Option<usize>]) -> Vec<usize> {
let valid: Vec<(usize, usize)> = positions
.iter()
.enumerate()
.filter_map(|(idx, &pos)| pos.map(|p| (idx, p)))
.collect();
if valid.is_empty() {
return Vec::new();
}
let n = valid.len();
let mut tails: Vec<usize> = Vec::with_capacity(n);
let mut prev: Vec<Option<usize>> = vec![None; n];
for i in 0..n {
let val = valid[i].1;
let pos = tails.partition_point(|&t| valid[t].1 < val);
if pos == tails.len() {
tails.push(i);
} else {
tails[pos] = i;
}
if pos > 0 {
prev[i] = Some(tails[pos - 1]);
}
}
let mut result = Vec::with_capacity(tails.len());
let mut idx = Some(*tails.last().unwrap());
while let Some(i) = idx {
result.push(valid[i].0);
idx = prev[i];
}
result.reverse();
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_longest_increasing_subsequence_basic() {
let positions = vec![Some(0), Some(1), Some(2), Some(3)];
let lis = longest_increasing_subsequence(&positions);
assert_eq!(lis, vec![0, 1, 2, 3]);
}
#[test]
fn test_longest_increasing_subsequence_reverse() {
let positions = vec![Some(3), Some(2), Some(1), Some(0)];
let lis = longest_increasing_subsequence(&positions);
assert_eq!(lis.len(), 1);
}
#[test]
fn test_longest_increasing_subsequence_mixed() {
let positions = vec![Some(0), Some(3), Some(1), Some(2)];
let lis = longest_increasing_subsequence(&positions);
assert_eq!(lis.len(), 3);
assert!(lis.contains(&0));
assert!(lis.contains(&2));
assert!(lis.contains(&3));
}
#[test]
fn test_longest_increasing_subsequence_with_new_items() {
let positions = vec![None, Some(0), None, Some(1)];
let lis = longest_increasing_subsequence(&positions);
assert_eq!(lis, vec![1, 3]); }
#[test]
fn test_longest_increasing_subsequence_empty() {
let positions: Vec<Option<usize>> = vec![];
let lis = longest_increasing_subsequence(&positions);
assert_eq!(lis, Vec::<usize>::new());
}
#[test]
fn test_longest_increasing_subsequence_all_new() {
let positions = vec![None, None, None];
let lis = longest_increasing_subsequence(&positions);
assert_eq!(lis, Vec::<usize>::new());
}
#[test]
fn test_lis_large_sequence() {
let positions: Vec<Option<usize>> = vec![
Some(3),
Some(1),
Some(4),
Some(1),
Some(5),
Some(9),
Some(2),
Some(6),
];
let lis = longest_increasing_subsequence(&positions);
assert_eq!(lis.len(), 4);
let values: Vec<usize> = lis.iter().map(|&i| positions[i].unwrap()).collect();
for w in values.windows(2) {
assert!(w[0] < w[1], "LIS must be strictly increasing: {:?}", values);
}
}
}