///|
pub fn contract_status_label(status : ContractStatus) -> String {
  match status {
    Match => "match"
    WidthMismatch => "width-mismatch"
    DirectionMismatch => "direction-mismatch"
    MissingPort => "missing-port"
    UnknownModule => "unknown-module"
    UnknownWidth => "unknown-width"
  }
}

///|
pub fn PortContract::label(self : PortContract) -> String {
  self.parent_module +
  "." +
  self.instance_name +
  "." +
  self.port_name +
  "=" +
  contract_status_label(self.status)
}

///|
pub fn SemanticIndex::contract_labels(self : SemanticIndex) -> Array[String] {
  self.port_contracts().map(fn(contract) { contract.label() })
}

///|
pub fn SemanticIndex::contract_table(self : SemanticIndex) -> String {
  let builder = StringBuilder()
  builder.write_string("PARENT\tINSTANCE\tPORT\tSTATUS\n")
  for contract in self.port_contracts() {
    builder.write_string(contract.parent_module)
    builder.write_char('\t')
    builder.write_string(contract.instance_name)
    builder.write_char('\t')
    builder.write_string(contract.port_name)
    builder.write_char('\t')
    builder.write_string(contract_status_label(contract.status))
    builder.write_char('\n')
  }
  builder.to_string()
}

///|
pub fn SemanticIndex::contract_status_histogram(self : SemanticIndex) -> String {
  let statuses = [
    Match,
    WidthMismatch,
    DirectionMismatch,
    MissingPort,
    UnknownModule,
    UnknownWidth,
  ]
  let parts : Array[String] = []
  for status in statuses {
    parts.push(
      contract_status_label(status) +
      "=" +
      self.contract_status_count(status).to_string(),
    )
  }
  parts.join(", ")
}