///|
fn encode_exemplar(exemplar : Exemplar) -> String {
let out = StringBuilder()
out.write_string(" # ")
if exemplar.labels.is_empty() {
out.write_string("{}")
} else {
out.write_string(encode_label_set(exemplar.labels))
}
out.write_char(' ')
out.write_string(format_number(exemplar.value))
match exemplar.timestamp {
Some(timestamp) => {
out.write_char(' ')
out.write_string(format_number(timestamp))
}
None => ()
}
out.to_string()
}
///|
fn encode_sample_with_number_label(
sample : Sample,
canonical_number_label : StringView,
) -> String {
let out = StringBuilder()
out.write_string(sample.name)
out.write_string(
encode_metric_label_set(sample.labels, canonical_number_label),
)
out.write_char(' ')
out.write_string(format_number(sample.value))
match sample.timestamp {
Some(timestamp) => {
out.write_char(' ')
out.write_string(format_number(timestamp))
}
None => ()
}
match sample.exemplar {
Some(exemplar) => out.write_string(encode_exemplar(exemplar))
None => ()
}
out.to_string()
}
///|
/// Encode one sample line without assuming a metric-family type.
pub fn encode_sample(sample : Sample) -> String {
encode_sample_with_number_label(sample, "")
}
///|
fn encode_family(family : MetricFamily, out : StringBuilder) -> Unit {
out.write_string("# TYPE ")
out.write_string(family.name)
out.write_char(' ')
out.write_string(family.metric_type.to_keyword())
out.write_char('\n')
match family.unit {
Some(unit) =>
if !unit.is_empty() {
out.write_string("# UNIT ")
out.write_string(family.name)
out.write_char(' ')
out.write_string(unit)
out.write_char('\n')
}
None => ()
}
match family.help {
Some(help) =>
if !help.is_empty() {
out.write_string("# HELP ")
out.write_string(family.name)
out.write_char(' ')
out.write_string(escape_help(help))
out.write_char('\n')
}
None => ()
}
let canonical_number_label = match family.metric_type {
Histogram | GaugeHistogram => "le"
Summary => "quantile"
Unknown | Counter | Gauge | Info | StateSet => ""
}
for sample in family.samples {
out.write_string(
encode_sample_with_number_label(sample, canonical_number_label),
)
out.write_char('\n')
}
}
///|
/// Encode a document in a stable canonical layout.
///
/// Family, sample and label order are preserved. Metadata is reordered as
/// TYPE, UNIT, HELP, then samples. The output ends with `# EOF` by default.
pub fn encode(document : Document, include_eof? : Bool = true) -> String {
let out = StringBuilder()
for family in document.families {
encode_family(family, out)
}
if include_eof {
out.write_string("# EOF\n")
}
out.to_string()
}
///|
/// Encode this document in canonical form.
pub fn Document::to_text(self : Document, include_eof? : Bool = true) -> String {
encode(self, include_eof~)
}
///|
/// Parse and immediately re-encode an exposition document.
pub fn normalize(input : StringView) -> Result[String, ParseError] {
match parse(input) {
Ok(document) => Ok(encode(document))
Err(error) => Err(error)
}
}