Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 24 additions & 26 deletions src/yaml_format_preserving.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ pub fn write_yaml_preserving_format(

// Check if there are unhandleable structural changes
// If we're adding new nested structures, fall back to standard serialization
if has_unhandleable_nested_changes(&original_value, &updated_value) {
if has_unhandleable_nested_changes(&original_value, updated_value) {
// For truly complex nested changes, use standard YAML serialization
return serde_yaml::to_string(updated_value)
.map_err(|e| format!("Failed to serialize YAML: {}", e));
}

// Collect keys that were removed (in original but not in updated)
let mut removed_keys = Vec::new();
collect_removed_keys(&original_value, &updated_value, "", &mut removed_keys);
collect_removed_keys(&original_value, updated_value, "", &mut removed_keys);

// Build a map of all keys and their new values
let updates = collect_all_changes(original_content, &updated_value)?;
let updates = collect_all_changes(original_content, updated_value)?;

if updates.is_empty() && removed_keys.is_empty() {
// No changes, return original
Expand Down Expand Up @@ -109,10 +109,11 @@ fn has_unhandleable_nested_changes(old: &Value, new: &Value) -> bool {
if let Some(new_val) = new_map.get(key) {
if old_val != new_val {
// If both are mappings and contents changed, we need to be careful
if old_val.is_mapping() && new_val.is_mapping() {
if has_unhandleable_nested_changes(old_val, new_val) {
return true;
}
if old_val.is_mapping()
&& new_val.is_mapping()
&& has_unhandleable_nested_changes(old_val, new_val)
{
return true;
}
}
}
Expand All @@ -126,29 +127,26 @@ fn has_unhandleable_nested_changes(old: &Value, new: &Value) -> bool {

/// Collects keys that were removed (in original but not in updated), including nested keys
fn collect_removed_keys(old: &Value, new: &Value, prefix: &str, removed: &mut Vec<String>) {
match (old, new) {
(Value::Mapping(old_map), Value::Mapping(new_map)) => {
for (key, old_val) in old_map {
if let Value::String(key_str) = key {
let full_key = if prefix.is_empty() {
key_str.clone()
} else {
format!("{}.{}", prefix, key_str)
};

if !new_map.contains_key(key) {
// Key was removed entirely
removed.push(full_key);
} else if let Some(new_val) = new_map.get(key) {
// Key exists in new, but might have removed nested keys
if old_val.is_mapping() && new_val.is_mapping() {
collect_removed_keys(old_val, new_val, &full_key, removed);
}
if let (Value::Mapping(old_map), Value::Mapping(new_map)) = (old, new) {
for (key, old_val) in old_map {
if let Value::String(key_str) = key {
let full_key = if prefix.is_empty() {
key_str.clone()
} else {
format!("{}.{}", prefix, key_str)
};

if !new_map.contains_key(key) {
// Key was removed entirely
removed.push(full_key);
} else if let Some(new_val) = new_map.get(key) {
// Key exists in new, but might have removed nested keys
if old_val.is_mapping() && new_val.is_mapping() {
collect_removed_keys(old_val, new_val, &full_key, removed);
}
}
}
}
_ => {}
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/yaml_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ pub fn get_value(value: &Value, path: &str) -> Result<Option<Value>, String> {
let mut current = value;
for part in parts {
if let Value::Mapping(map) = current {
match map.get(&Value::String(part.to_string())) {
match map.get(Value::String(part.to_string())) {
Some(next) => current = next,
None => return Ok(None),
}
Expand Down Expand Up @@ -263,14 +263,14 @@ fn unset_at_path(value: &mut Value, path: &str) -> Result<(), String> {
if parts.len() == 1 {
// Direct child: remove from root mapping
if let Value::Mapping(ref mut map) = value {
map.remove(&Value::String(parts[0].to_string()));
map.remove(Value::String(parts[0].to_string()));
}
} else {
// Navigate to parent, then remove the final key
let mut current = value;
for &part in parts[..parts.len() - 1].iter() {
if let Value::Mapping(ref mut map) = current {
if let Some(next) = map.get_mut(&Value::String(part.to_string())) {
if let Some(next) = map.get_mut(Value::String(part.to_string())) {
current = next;
} else {
// Path doesn't exist
Expand All @@ -284,7 +284,7 @@ fn unset_at_path(value: &mut Value, path: &str) -> Result<(), String> {

// Remove the final key
if let Value::Mapping(ref mut map) = current {
map.remove(&Value::String(parts[parts.len() - 1].to_string()));
map.remove(Value::String(parts[parts.len() - 1].to_string()));
}
}

Expand Down