// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0
///|
/// The exposition formats this library writes and reads.
pub(all) enum Format {
/// Prometheus text exposition format, version 0.0.4.
Text004
/// OpenMetrics text format, version 1.0.0.
OpenMetrics100
}
///|
/// The `Content-Type` an HTTP response should carry for this format.
pub fn Format::content_type(self : Format) -> String {
match self {
Format::Text004 => "text/plain; version=0.0.4; charset=utf-8"
Format::OpenMetrics100 =>
"application/openmetrics-text; version=1.0.0; charset=utf-8"
}
}
///|
fn first_int(s : String) -> Int32 {
let mut n = 0
let mut any = false
for c in s {
if c >= '0' && c <= '9' {
n = n * 10 + (c.to_int() - '0'.to_int())
any = true
} else {
break
}
}
if any {
n
} else {
0
}
}
///|
fn media_type(part : String) -> String {
let mut end = part.length()
for i, c in part {
if c == ';' {
end = i
break
}
}
part[0:end].to_owned().trim().to_owned()
}
///|
fn parameter(part : String, key : String) -> String? {
for piece in part.split(";") {
let p = piece.to_owned().trim().to_owned()
let mut eq = -1
for i, c in p {
if c == '=' {
eq = i
break
}
}
if eq >= 0 {
let k = p[0:eq].to_owned().trim().to_owned()
if k == key {
return Some(p[eq + 1:].to_owned().trim().to_owned())
}
}
}
None
}
///|
fn version_at_least_one(version : String) -> Bool {
let parts = version.split(".").collect()
if parts.length() == 0 {
return false
}
first_int(parts[0].to_owned()) >= 1
}
///|
/// Pick a format from an HTTP `Accept` header. `application/openmetrics-text`
/// wins when it names a version this library speaks (or names no version);
/// otherwise a scrape gets the compatibility format, which is also the default
/// when the header is absent or unrecognised.
pub fn negotiate(accept : String) -> Format {
for part in accept.split(",") {
let p = part.to_owned()
let m = media_type(p)
if m == "application/openmetrics-text" {
match parameter(p, "version") {
Some(v) => if version_at_least_one(v) { return Format::OpenMetrics100 }
None => return Format::OpenMetrics100
}
} else if m == "text/plain" {
return Format::Text004
}
}
Format::Text004
}