///|
pub fn SemanticIndex::matching_contracts(
  self : SemanticIndex,
) -> Array[PortContract] {
  self.port_contracts().filter(fn(contract) { contract.status is Match })
}

///|
pub fn SemanticIndex::mismatching_contracts(
  self : SemanticIndex,
) -> Array[PortContract] {
  self
  .port_contracts()
  .filter(fn(contract) {
    contract.status is WidthMismatch || contract.status is DirectionMismatch
  })
}

///|
pub fn SemanticIndex::unresolved_contracts(
  self : SemanticIndex,
) -> Array[PortContract] {
  self
  .port_contracts()
  .filter(fn(contract) {
    contract.status is UnknownModule || contract.status is MissingPort
  })
}

///|
pub fn SemanticIndex::contract_summary(self : SemanticIndex) -> String {
  "total=" +
  self.port_contracts().length().to_string() +
  ", matching=" +
  self.matching_contracts().length().to_string() +
  ", mismatching=" +
  self.mismatching_contracts().length().to_string() +
  ", unresolved=" +
  self.unresolved_contracts().length().to_string()
}

///|
pub fn SemanticIndex::contract_for_instance(
  self : SemanticIndex,
  parent : String,
  instance : String,
) -> Array[PortContract] {
  self
  .port_contracts()
  .filter(fn(contract) {
    contract.parent_module == parent && contract.instance_name == instance
  })
}

///|
pub fn SemanticIndex::contract_statuses(
  self : SemanticIndex,
) -> Array[ContractStatus] {
  self.port_contracts().map(fn(contract) { contract.status })
}

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

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