unified merge
All checks were successful
Version Check / check-version (pull_request) Successful in 3s
All checks were successful
Version Check / check-version (pull_request) Successful in 3s
This commit is contained in:
@@ -35,53 +35,6 @@ impl FormatHandler for EnvHandler {
|
||||
Ok(vars)
|
||||
}
|
||||
|
||||
fn merge(&self, path: &Path, vars: &mut Vec<ConfigItem>) -> io::Result<()> {
|
||||
if !path.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let content = fs::read_to_string(path)?;
|
||||
for line in content.lines() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some((key, val)) = line.split_once('=') {
|
||||
let key = key.trim();
|
||||
let parsed_val = val.trim().trim_matches('"').trim_matches('\'').to_string();
|
||||
|
||||
if let Some(var) = vars.iter_mut().find(|v| v.key == key) {
|
||||
if var.value.as_deref() != Some(&parsed_val) {
|
||||
var.value = Some(parsed_val);
|
||||
var.status = ItemStatus::Modified;
|
||||
}
|
||||
} else {
|
||||
vars.push(ConfigItem {
|
||||
key: key.to_string(),
|
||||
path: key.to_string(),
|
||||
value: Some(parsed_val),
|
||||
template_value: None,
|
||||
default_value: None,
|
||||
depth: 0,
|
||||
is_group: false,
|
||||
status: ItemStatus::MissingFromTemplate,
|
||||
value_type: ValueType::String,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark missing from active
|
||||
for var in vars.iter_mut() {
|
||||
if var.status == ItemStatus::Present && var.value.is_none() {
|
||||
var.status = ItemStatus::MissingFromActive;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&self, path: &Path, vars: &[ConfigItem]) -> io::Result<()> {
|
||||
let mut file = fs::File::create(path)?;
|
||||
for var in vars {
|
||||
@@ -126,27 +79,36 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_merge_env() {
|
||||
let mut example_file = NamedTempFile::new().unwrap();
|
||||
writeln!(example_file, "KEY1=default1\nKEY2=default2").unwrap();
|
||||
let handler = EnvHandler;
|
||||
let mut vars = handler.parse(example_file.path()).unwrap();
|
||||
|
||||
let mut env_file = NamedTempFile::new().unwrap();
|
||||
writeln!(env_file, "KEY1=custom1\nKEY3=custom3").unwrap();
|
||||
let mut vars = handler.parse(env_file.path()).unwrap(); // Active vars
|
||||
|
||||
handler.merge(env_file.path(), &mut vars).unwrap();
|
||||
let mut example_file = NamedTempFile::new().unwrap();
|
||||
writeln!(example_file, "KEY1=default1\nKEY2=default2").unwrap();
|
||||
|
||||
handler.merge(example_file.path(), &mut vars).unwrap(); // Merge template into active
|
||||
|
||||
// Should preserve order of active, then append template
|
||||
assert_eq!(vars.len(), 3);
|
||||
|
||||
// Active key that exists in template
|
||||
assert_eq!(vars[0].key, "KEY1");
|
||||
assert_eq!(vars[0].value.as_deref(), Some("custom1"));
|
||||
assert_eq!(vars[0].value.as_deref(), Some("custom1")); // Keeps active value
|
||||
assert_eq!(vars[0].template_value.as_deref(), Some("default1")); // Gets template default
|
||||
assert_eq!(vars[0].status, ItemStatus::Modified);
|
||||
|
||||
assert_eq!(vars[1].key, "KEY2");
|
||||
assert_eq!(vars[1].value.as_deref(), Some("default2"));
|
||||
// Active key that DOES NOT exist in template
|
||||
assert_eq!(vars[1].key, "KEY3");
|
||||
assert_eq!(vars[1].value.as_deref(), Some("custom3"));
|
||||
assert_eq!(vars[1].status, ItemStatus::Present);
|
||||
|
||||
assert_eq!(vars[2].key, "KEY3");
|
||||
assert_eq!(vars[2].value.as_deref(), Some("custom3"));
|
||||
assert_eq!(vars[2].status, ItemStatus::MissingFromTemplate);
|
||||
// Template key that DOES NOT exist in active
|
||||
assert_eq!(vars[2].key, "KEY2");
|
||||
assert_eq!(vars[2].value.as_deref(), None); // Missing from active
|
||||
assert_eq!(vars[2].template_value.as_deref(), Some("default2"));
|
||||
assert_eq!(vars[2].status, ItemStatus::MissingFromActive);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -283,43 +283,6 @@ impl FormatHandler for HierarchicalHandler {
|
||||
Ok(vars)
|
||||
}
|
||||
|
||||
fn merge(&self, path: &Path, vars: &mut Vec<ConfigItem>) -> io::Result<()> {
|
||||
if !path.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
let existing_value = self.read_value(path)?;
|
||||
let mut existing_vars = Vec::new();
|
||||
flatten(&existing_value, "", 0, "", &mut existing_vars);
|
||||
|
||||
for var in vars.iter_mut() {
|
||||
if let Some(existing) = existing_vars.iter().find(|v| v.path == var.path) {
|
||||
if var.value != existing.value {
|
||||
var.value = existing.value.clone();
|
||||
var.status = ItemStatus::Modified;
|
||||
}
|
||||
} else {
|
||||
var.status = ItemStatus::MissingFromActive;
|
||||
}
|
||||
}
|
||||
|
||||
// Find keys in active that are not in template
|
||||
let mut to_add = Vec::new();
|
||||
for existing in existing_vars {
|
||||
if !vars.iter().any(|v| v.path == existing.path) {
|
||||
let mut new_item = existing.clone();
|
||||
new_item.status = ItemStatus::MissingFromTemplate;
|
||||
new_item.template_value = None;
|
||||
new_item.default_value = None;
|
||||
to_add.push(new_item);
|
||||
}
|
||||
}
|
||||
|
||||
// Basic insertion logic for extra keys (could be improved to insert at correct depth/position)
|
||||
vars.extend(to_add);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&self, path: &Path, vars: &[ConfigItem]) -> io::Result<()> {
|
||||
let mut root = Value::Object(Map::new());
|
||||
for var in vars {
|
||||
|
||||
@@ -52,38 +52,6 @@ impl FormatHandler for IniHandler {
|
||||
Ok(vars)
|
||||
}
|
||||
|
||||
fn merge(&self, path: &Path, vars: &mut Vec<ConfigItem>) -> io::Result<()> {
|
||||
if !path.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let existing_vars = self.parse(path).unwrap_or_default();
|
||||
|
||||
for var in vars.iter_mut() {
|
||||
if let Some(existing) = existing_vars.iter().find(|v| v.path == var.path) {
|
||||
if var.value != existing.value {
|
||||
var.value = existing.value.clone();
|
||||
var.status = ItemStatus::Modified;
|
||||
}
|
||||
} else {
|
||||
var.status = ItemStatus::MissingFromActive;
|
||||
}
|
||||
}
|
||||
|
||||
// Add items from active that are not in template
|
||||
for existing in existing_vars {
|
||||
if !vars.iter().any(|v| v.path == existing.path) {
|
||||
let mut new_item = existing.clone();
|
||||
new_item.status = ItemStatus::MissingFromTemplate;
|
||||
new_item.template_value = None;
|
||||
new_item.default_value = None;
|
||||
vars.push(new_item);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&self, path: &Path, vars: &[ConfigItem]) -> io::Result<()> {
|
||||
let mut conf = Ini::new();
|
||||
for var in vars {
|
||||
|
||||
@@ -10,7 +10,6 @@ pub mod properties;
|
||||
pub enum ItemStatus {
|
||||
Present,
|
||||
MissingFromActive,
|
||||
MissingFromTemplate,
|
||||
Modified,
|
||||
}
|
||||
|
||||
@@ -48,7 +47,41 @@ pub enum FormatType {
|
||||
|
||||
pub trait FormatHandler {
|
||||
fn parse(&self, path: &Path) -> io::Result<Vec<ConfigItem>>;
|
||||
fn merge(&self, path: &Path, vars: &mut Vec<ConfigItem>) -> io::Result<()>;
|
||||
fn merge(&self, path: &Path, vars: &mut Vec<ConfigItem>) -> io::Result<()> {
|
||||
if !path.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let template_vars = self.parse(path).unwrap_or_default();
|
||||
|
||||
for var in vars.iter_mut() {
|
||||
if let Some(template_var) = template_vars.iter().find(|v| v.path == var.path) {
|
||||
var.template_value = template_var.value.clone();
|
||||
var.default_value = template_var.value.clone();
|
||||
|
||||
if var.value != template_var.value {
|
||||
var.status = ItemStatus::Modified;
|
||||
} else {
|
||||
var.status = ItemStatus::Present;
|
||||
}
|
||||
} else {
|
||||
// Exists in active, but not in template
|
||||
var.status = ItemStatus::Present;
|
||||
}
|
||||
}
|
||||
|
||||
// Add items from template that are missing in active
|
||||
for template_var in template_vars {
|
||||
if !vars.iter().any(|v| v.path == template_var.path) {
|
||||
let mut new_item = template_var.clone();
|
||||
new_item.status = ItemStatus::MissingFromActive;
|
||||
new_item.value = None;
|
||||
vars.push(new_item);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn write(&self, path: &Path, vars: &[ConfigItem]) -> io::Result<()>;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,38 +61,6 @@ impl FormatHandler for PropertiesHandler {
|
||||
Ok(vars)
|
||||
}
|
||||
|
||||
fn merge(&self, path: &Path, vars: &mut Vec<ConfigItem>) -> io::Result<()> {
|
||||
if !path.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let existing_vars = self.parse(path).unwrap_or_default();
|
||||
|
||||
for var in vars.iter_mut() {
|
||||
if let Some(existing) = existing_vars.iter().find(|v| v.path == var.path) {
|
||||
if var.value != existing.value {
|
||||
var.value = existing.value.clone();
|
||||
var.status = ItemStatus::Modified;
|
||||
}
|
||||
} else {
|
||||
var.status = ItemStatus::MissingFromActive;
|
||||
}
|
||||
}
|
||||
|
||||
// Add items from active that are not in template
|
||||
for existing in existing_vars {
|
||||
if !vars.iter().any(|v| v.path == existing.path) {
|
||||
let mut new_item = existing.clone();
|
||||
new_item.status = ItemStatus::MissingFromTemplate;
|
||||
new_item.template_value = None;
|
||||
new_item.default_value = None;
|
||||
vars.push(new_item);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(vars.sort_by(|a, b| a.path.cmp(&b.path)))
|
||||
}
|
||||
|
||||
fn write(&self, path: &Path, vars: &[ConfigItem]) -> io::Result<()> {
|
||||
let mut props = HashMap::new();
|
||||
for var in vars {
|
||||
|
||||
Reference in New Issue
Block a user