///|
/// Require the descriptor kind used by one concrete vector type.
fn expect_kind(
  descriptor : @metric.Descriptor,
  expected : @metric.MetricKind,
) -> Unit raise VectorError {
  if descriptor.kind() != expected {
    raise MetricKindMismatch
  }
}

///|
/// Optional MoonBit arguments take their payload at the call site. This
/// helper preserves the public `Int?` vector configuration while constructing
/// the schema through the two valid call forms.
fn make_schema(
  descriptor : @metric.Descriptor,
  cardinality_limit : Int?,
) -> LabelSchema raise VectorError {
  match cardinality_limit {
    Some(limit) =>
      LabelSchema::new(
        descriptor.variable_label_names(),
        cardinality_limit=limit,
      )
    None => LabelSchema::new(descriptor.variable_label_names())
  }
}

///|
/// Build the fully bound child labels and put their exposition order in a
/// canonical form. A child owns this copy; later changes to caller arrays or
/// maps cannot alter its identity.
fn child_labels(
  descriptor : @metric.Descriptor,
  schema : LabelSchema,
  values : Array[String],
) -> Array[@metric.Label] {
  let labels = descriptor.const_labels()
  let names = schema.names()
  for index in 0.. left.name.compare(right.name))
  labels
}

///|
/// Check the limit at the same logical point as inserting a new child.
fn ensure_capacity(
  cardinality_limit : Int?,
  current : Int,
) -> Unit raise VectorError {
  match cardinality_limit {
    Some(limit) if current >= limit =>
      raise CardinalityLimitExceeded(limit~, current~)
    _ => ()
  }
}

///|
fn make_counter_child(
  descriptor : @metric.Descriptor,
  labels : Array[@metric.Label],
) -> @metric.Counter raise VectorError {
  match descriptor.unit() {
    Some(unit) =>
      @metric.Counter::new(
        descriptor.name(),
        help=descriptor.help(),
        unit~,
        const_labels=labels,
      ) catch {
        _ => raise MetricConstructionFailed(descriptor.name())
      }
    None =>
      @metric.Counter::new(
        descriptor.name(),
        help=descriptor.help(),
        const_labels=labels,
      ) catch {
        _ => raise MetricConstructionFailed(descriptor.name())
      }
  }
}

///|
fn make_gauge_child(
  descriptor : @metric.Descriptor,
  labels : Array[@metric.Label],
) -> @metric.Gauge raise VectorError {
  match descriptor.unit() {
    Some(unit) =>
      @metric.Gauge::new(
        descriptor.name(),
        help=descriptor.help(),
        unit~,
        const_labels=labels,
      ) catch {
        _ => raise MetricConstructionFailed(descriptor.name())
      }
    None =>
      @metric.Gauge::new(
        descriptor.name(),
        help=descriptor.help(),
        const_labels=labels,
      ) catch {
        _ => raise MetricConstructionFailed(descriptor.name())
      }
  }
}

///|
fn make_histogram_child(
  descriptor : @metric.Descriptor,
  boundaries : Array[Double],
  labels : Array[@metric.Label],
) -> @metric.Histogram raise VectorError {
  match descriptor.unit() {
    Some(unit) =>
      @metric.Histogram::new(
        descriptor.name(),
        boundaries,
        help=descriptor.help(),
        unit~,
        const_labels=labels,
      ) catch {
        _ => raise MetricConstructionFailed(descriptor.name())
      }
    None =>
      @metric.Histogram::new(
        descriptor.name(),
        boundaries,
        help=descriptor.help(),
        const_labels=labels,
      ) catch {
        _ => raise MetricConstructionFailed(descriptor.name())
      }
  }
}