///|
pub fn SemanticIndex::ir_has_modules(self : SemanticIndex) -> Bool {
  self.module_count() > 0
}

///|
pub fn SemanticIndex::ir_has_issues(self : SemanticIndex) -> Bool {
  self.issues.length() > 0
}

///|
pub fn SemanticIndex::ir_has_roots(self : SemanticIndex) -> Bool {
  self.hierarchy_roots().length() > 0
}

///|
pub fn SemanticIndex::ir_has_cycles(self : SemanticIndex) -> Bool {
  !self.graph_is_acyclic()
}

///|
pub fn SemanticIndex::ir_checklist(
  self : SemanticIndex,
) -> Array[(String, Bool)] {
  [
    ("has-modules", self.ir_has_modules()),
    ("has-roots", self.ir_has_roots()),
    ("acyclic", self.graph_is_acyclic()),
    ("release-ready", self.release_ready()),
  ]
}

///|
pub fn SemanticIndex::ir_checklist_summary(self : SemanticIndex) -> String {
  let parts : Array[String] = []
  for item in self.ir_checklist() {
    let (name, value) = item
    parts.push(name + "=" + value.to_string())
  }
  parts.join(", ")
}

///|
pub fn SemanticIndex::ir_is_valid(self : SemanticIndex) -> Bool {
  self.ir_has_modules() && self.ir_has_roots() && !self.ir_has_cycles()
}

///|
pub fn SemanticIndex::ir_status(self : SemanticIndex) -> String {
  if self.ir_is_valid() {
    "valid"
  } else if self.ir_has_issues() {
    "issues"
  } else {
    "incomplete"
  }
}

///|
pub fn SemanticIndex::ir_status_line(self : SemanticIndex) -> String {
  "status=" + self.ir_status() + ", " + self.ir_checklist_summary()
}

///|
pub fn SemanticIndex::ir_module_status(
  self : SemanticIndex,
  name : String,
) -> String {
  match self.module_json(name) {
    Some(_) => name + ":present"
    None => name + ":missing"
  }
}

///|
pub fn SemanticIndex::ir_module_statuses(self : SemanticIndex) -> Array[String] {
  self.module_names().map(fn(name) { self.ir_module_status(name) })
}

///|
pub fn SemanticIndex::ir_module_status_summary(self : SemanticIndex) -> String {
  self.ir_module_statuses().join(", ")
}

///|
pub fn SemanticIndex::ir_module_status_count(
  self : SemanticIndex,
  status : String,
) -> Int {
  let mut count = 0
  for value in self.ir_module_statuses() {
    if value.has_suffix(status) {
      count += 1
    }
  }
  count
}

///|
pub fn SemanticIndex::ir_has_module_status(
  self : SemanticIndex,
  status : String,
) -> Bool {
  self.ir_module_status_count(status) > 0
}

///|
pub fn SemanticIndex::ir_is_empty(self : SemanticIndex) -> Bool {
  !self.ir_has_modules()
}

///|
pub fn SemanticIndex::ir_has_edges(self : SemanticIndex) -> Bool {
  self.edge_count() > 0
}

///|
pub fn SemanticIndex::ir_node_count(self : SemanticIndex) -> Int {
  self.dependency_nodes().length()
}

///|
pub fn SemanticIndex::ir_edge_summary(self : SemanticIndex) -> String {
  "nodes=" +
  self.ir_node_count().to_string() +
  ", edges=" +
  self.edge_count().to_string()
}