///|
pub(all) struct ConfigChange {
name : String
before : Value
after : Value
} derive(Eq, Debug)
///|
pub(all) struct OverlayConflict {
name : String
previous : Value
replacement : Value
fragment_index : Int32
} derive(Eq, Debug)
///|
/// Report only value-changing overrides, preserving input order.
pub fn overlay_conflicts(fragments : Array[Fragment]) -> Array[OverlayConflict] {
let seen : Map[String, Value] = Map([])
let out = []
for index, fragment in fragments {
for (name, value) in fragment.entries {
match seen.get(name) {
Some(previous) =>
if previous != value {
out.push({
name,
previous,
replacement: value,
fragment_index: index,
})
}
None => ()
}
seen[name] = value
}
}
out
}
///|
pub fn Schema::changes(
self : Schema,
before : Resolution,
after : Resolution,
) -> Array[ConfigChange] raise ConfigError {
self.check_snapshot(before)
self.check_snapshot(after)
let out = []
for name in self.names() {
let a = before.value(name)
let b = after.value(name)
if a != b {
out.push({ name, before: a, after: b, })
}
}
out
}
///|
fn same_values(
schema : Schema,
a : Resolution,
b : Resolution,
) -> Bool raise ConfigError {
for name in schema.names() {
if a.value(name) != b.value(name) {
return false
}
}
true
}
///|
/// Greedy irredundant fragment, not a globally minimum cardinality solution.
/// Limited to 256 symbols because each candidate requires a full resolution.
pub fn Schema::minimal_fragment(
self : Schema,
target : Resolution,
) -> Fragment raise ConfigError {
self.check_snapshot(target)
if self.defs.length() > 256 {
raise Invalid("fragment minimization supports at most 256 symbols")
}
let mut candidate = Fragment::new()
for name in self.names() {
let value = target.value(name)
if target.visibility(name) != N && value != Unset {
self.request(candidate, name, value)
}
}
if !same_values(self, self.resolve(candidate), target) {
raise Invalid("target cannot be reproduced by visible requests")
}
let mut cursor = 0
while cursor < candidate.entries.length() {
let reduced = Fragment::new()
for index, entry in candidate.entries {
if index != cursor {
reduced.entries.push(entry)
}
}
if same_values(self, self.resolve(reduced), target) {
candidate = reduced
} else {
cursor += 1
}
}
candidate
}
///|
pub fn Schema::export_fragment(
self : Schema,
fragment : Fragment,
) -> String raise ConfigError {
let out = []
for (name, value) in fragment.entries {
let kind = self.kind(name)
if !kind.accepts(value) {
raise Invalid("fragment/schema type mismatch")
}
out.push(config_line(name, kind, value))
}
out.join("")
}
///|
#deprecated
pub extend ConfigChange with @debug.Debug::{to_repr}
///|
#deprecated
pub extend ConfigChange with Eq::{equal, not_equal}
///|
#deprecated
pub extend OverlayConflict with @debug.Debug::{to_repr}
///|
#deprecated
pub extend OverlayConflict with Eq::{equal, not_equal}