///|
fn sample_error(
line : Int,
cursor : TextCursor,
message : String,
) -> Result[Sample, ParseError] {
Err(ParseError::new(line, cursor.position() + 1, message))
}
///|
fn parse_exemplar_from(
cursor : TextCursor,
line : Int,
) -> Result[Exemplar, ParseError] {
if !cursor.consume('#') {
return Err(
ParseError::new(line, cursor.position() + 1, "expected exemplar marker"),
)
}
if !cursor.take_horizontal_space() {
return Err(
ParseError::new(
line,
cursor.position() + 1,
"expected space after exemplar marker",
),
)
}
let labels = match parse_labels_from(cursor) {
Ok(labels) => labels
Err(error) =>
return Err(ParseError::new(line, cursor.position() + 1, error))
}
if !cursor.take_horizontal_space() {
return Err(
ParseError::new(line, cursor.position() + 1, "expected exemplar value"),
)
}
let value_text = cursor.read_token()
let value = match parse_number(value_text) {
Ok(value) => value
Err(error) =>
return Err(ParseError::new(line, cursor.position() + 1, error))
}
let mut timestamp : Double? = None
if cursor.take_horizontal_space() {
if !cursor.is_end() {
let timestamp_text = cursor.read_token()
timestamp = match parse_timestamp(timestamp_text) {
Ok(value) => Some(value)
Err(error) =>
return Err(ParseError::new(line, cursor.position() + 1, error))
}
cursor.skip_horizontal_space()
}
}
if !cursor.is_end() {
return Err(
ParseError::new(
line,
cursor.position() + 1,
"unexpected text after exemplar",
),
)
}
Ok(Exemplar::new(labels, value, timestamp?))
}
///|
/// Parse one complete OpenMetrics sample line.
///
/// The accepted shape is:
/// `name{labels} value [timestamp] [# {exemplar_labels} value [timestamp]]`.
pub fn parse_sample_line(
input : StringView,
line? : Int = 1,
) -> Result[Sample, ParseError] {
let cursor = TextCursor::new(input)
let name = cursor.read_metric_name()
if !is_valid_metric_name(name) {
return sample_error(line, cursor, "invalid or missing metric name")
}
let labels = if cursor.peek() == Some('{') {
match parse_labels_from(cursor) {
Ok(labels) => labels
Err(error) => return sample_error(line, cursor, error)
}
} else {
[]
}
if !cursor.take_horizontal_space() {
return sample_error(line, cursor, "expected space before sample value")
}
let value_text = cursor.read_token()
if value_text == "" {
return sample_error(line, cursor, "missing sample value")
}
let value = match parse_number(value_text) {
Ok(value) => value
Err(error) => return sample_error(line, cursor, error)
}
let mut timestamp : Double? = None
let mut exemplar : Exemplar? = None
if cursor.take_horizontal_space() && !cursor.is_end() {
if cursor.peek() == Some('#') {
exemplar = match parse_exemplar_from(cursor, line) {
Ok(value) => Some(value)
Err(error) => return Err(error)
}
} else {
let timestamp_text = cursor.read_token()
timestamp = match parse_timestamp(timestamp_text) {
Ok(value) => Some(value)
Err(error) => return sample_error(line, cursor, error)
}
if cursor.take_horizontal_space() && !cursor.is_end() {
if cursor.peek() != Some('#') {
return sample_error(line, cursor, "unexpected text after timestamp")
}
exemplar = match parse_exemplar_from(cursor, line) {
Ok(value) => Some(value)
Err(error) => return Err(error)
}
}
}
}
cursor.skip_horizontal_space()
if !cursor.is_end() {
return sample_error(line, cursor, "unexpected text after sample")
}
Ok(Sample::new(name, value, labels~, timestamp?, exemplar?))
}