///|
/// Return a group with a member inserted or replaced. Existing order is retained;
/// new names append at the end. Unchanged values are shared with the source.
pub fn Value::with_member(
self : Value,
name : String,
value : Value,
) -> Value raise ConfigError {
guard self is Group(items) else {
raise failure("type_mismatch", "expected group")
}
check_name(name)
let output = items.copy()
for i = 0; i < output.length(); i = i + 1 {
if output[i].0 == name {
output[i] = (name, value)
return Group(output)
}
}
output.push((name, value))
Group(output)
}
///|
/// Return a group without an existing member; missing names are errors.
/// Unchanged values are shared; no source container is modified.
pub fn Value::without_member(
self : Value,
name : String,
) -> Value raise ConfigError {
guard self is Group(items) else {
raise failure("type_mismatch", "expected group")
}
let output : Array[(String, Value)] = []
let mut found = false
for (key, value) in items {
if key == name {
found = true
} else {
output.push((key, value))
}
}
if !found {
raise failure("missing_member", "missing setting: " + name)
}
Group(output)
}
///|
fn check_name(name : String) -> Unit raise ConfigError {
let bytes = @utf8.encode(name)
if bytes.is_empty() || !name_start(bytes[0].to_int()) {
raise failure("invalid_name", "invalid setting name")
}
for b in bytes {
if !name_part(b.to_int()) {
raise failure("invalid_name", "invalid setting name")
}
}
}