///|
/// Select a semantic state from a two-clock valid-time timeline.
pub(all) enum SpanFilter {
Assigned
Equals(String)
Withdrawn
Unclaimed
}
///|
fn SpanFilter::accepts(self : SpanFilter, segment : Segment) -> Bool {
match self {
Assigned => segment.value() is Some(_)
Equals(expected) => segment.value() == Some(expected)
Withdrawn => segment.is_deleted()
Unclaimed => segment.is_gap()
}
}
///|
/// Return maximal spans matching a semantic state, merging adjacent matching
/// segments even if their winning revision identities differ.
pub fn Ledger::matching_spans(
self : Ledger,
key : String,
bounds : Span,
known_at : Int64,
filter : SpanFilter,
max_segments? : Int = 4096,
) -> Array[Span] raise TemporalError {
if max_segments <= 0 {
raise TooManySegments(maximum=max_segments)
}
let timeline = self.timeline(key, bounds, known_at, max_segments~)
let output : Array[Span] = []
for segment in timeline {
if filter.accepts(segment) {
if !output.is_empty() &&
output[output.length() - 1].touches(segment.valid) {
let last = output.length() - 1
output[last] = { start: output[last].start, end: segment.valid.end, }
} else {
if output.length() >= max_segments {
raise TooManySegments(maximum=max_segments)
}
output.push(segment.valid)
}
}
}
output
}