///|
priv struct CleanFrame {
id : String
steps : Array[ResolveStep]
mut next : Int
mut clean : Bool
}
///|
// 1 = active, 2 = proven clean, 3 = requires the full diagnostic traversal.
// Any conflict field is conservative: its meaning depends on a root's selected
// closure, so it must not be checked against the whole directory at once.
fn QueryContext::clean_subgraphs(self : QueryContext) -> Map[String, Int] {
let states : Map[String, Int] = Map([])
for root in self.packages.package_ids {
if states.contains(root) {
continue
}
let frames : Array[CleanFrame] = [self.clean_frame(root)]
states[root] = 1
while !frames.is_empty() {
let frame = frames[frames.length() - 1]
if frame.next == frame.steps.length() {
states[frame.id] = if frame.clean { 2 } else { 3 }
frames.pop() |> ignore
if !frame.clean && !frames.is_empty() {
frames[frames.length() - 1].clean = false
}
continue
}
let step = frame.steps[frame.next]
frame.next = frame.next + 1
match step {
Problem(_) | Unavailable(_) => frame.clean = false
Visit(id) =>
match states.get(id) {
Some(2) => ()
Some(_) => frame.clean = false
None => {
states[id] = 1
frames.push(self.clean_frame(id))
}
}
}
}
}
states
}
///|
fn QueryContext::clean_frame(self : QueryContext, id : String) -> CleanFrame {
{
id,
steps: self.resolve_steps(id),
next: 0,
clean: self.packages.packages.get(id).unwrap().document.field("Conflicts")
is None,
}
}