// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0

///|
fn is_ascii_alpha(c : Char) -> Bool {
  (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}

///|
fn is_ascii_digit(c : Char) -> Bool {
  c >= '0' && c <= '9'
}

///|
/// Whether `name` is a metric name under the OpenMetrics character rules:
/// `[a-zA-Z_:][a-zA-Z0-9_:]*`.
pub fn is_valid_metric_name(name : String) -> Bool {
  if name.length() == 0 {
    return false
  }
  let mut first = true
  for c in name {
    let ok = if first {
      is_ascii_alpha(c) || c == '_' || c == ':'
    } else {
      is_ascii_alpha(c) || is_ascii_digit(c) || c == '_' || c == ':'
    }
    if !ok {
      return false
    }
    first = false
  }
  true
}

///|
/// Whether `name` is a label name under the OpenMetrics character rules:
/// `[a-zA-Z_][a-zA-Z0-9_]*`.
pub fn is_valid_label_name(name : String) -> Bool {
  if name.length() == 0 {
    return false
  }
  let mut first = true
  for c in name {
    let ok = if first {
      is_ascii_alpha(c) || c == '_'
    } else {
      is_ascii_alpha(c) || is_ascii_digit(c) || c == '_'
    }
    if !ok {
      return false
    }
    first = false
  }
  true
}

///|
/// Whether `unit` may be appended to a metric name. A unit starts with a letter
/// and continues with letters, digits, or underscores.
pub fn is_valid_unit(unit : String) -> Bool {
  if unit.length() == 0 {
    return false
  }
  let mut first = true
  for c in unit {
    let ok = if first {
      is_ascii_alpha(c)
    } else {
      is_ascii_alpha(c) || is_ascii_digit(c) || c == '_'
    }
    if !ok {
      return false
    }
    first = false
  }
  true
}

///|
/// Refuse a value that is not a metric name.
fn check_metric_name(name : String) -> Unit raise Refused {
  if !is_valid_metric_name(name) {
    raise Refused("invalid metric name: " + name)
  }
}

///|
/// Refuse a value that is not a label name.
fn check_label_name(name : String) -> Unit raise Refused {
  if !is_valid_label_name(name) {
    raise Refused("invalid label name: " + name)
  }
}

///|
/// Refuse a value that is not a unit.
fn check_unit(unit : String) -> Unit raise Refused {
  if !is_valid_unit(unit) {
    raise Refused("invalid unit: " + unit)
  }
}