use crate::level::Level;
use crate::value::Value;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Field<'a> {
pub key: &'a str,
pub value: Value<'a>,
}
impl<'a> Field<'a> {
pub fn new<V: Into<Value<'a>>>(key: &'a str, value: V) -> Self {
Self {
key,
value: value.into(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Metadata<'a> {
pub level: Level,
pub target: &'a str,
pub file: Option<&'a str>,
pub line: Option<u32>,
pub timestamp_unix_nanos: Option<u128>,
}
impl<'a> Metadata<'a> {
pub const fn new(level: Level, target: &'a str) -> Self {
Self {
level,
target,
file: None,
line: None,
timestamp_unix_nanos: None,
}
}
pub const fn with_location(mut self, file: &'a str, line: u32) -> Self {
self.file = Some(file);
self.line = Some(line);
self
}
pub const fn with_timestamp(mut self, ts_unix_nanos: u128) -> Self {
self.timestamp_unix_nanos = Some(ts_unix_nanos);
self
}
}
#[derive(Debug, Clone, Copy)]
pub struct Record<'a> {
pub metadata: Metadata<'a>,
pub message: &'a str,
pub fields: &'a [Field<'a>],
pub context: &'a [Field<'a>],
}
impl<'a> Record<'a> {
pub const fn new(metadata: Metadata<'a>, message: &'a str, fields: &'a [Field<'a>]) -> Self {
Self {
metadata,
message,
fields,
context: &[],
}
}
pub const fn with_context(
metadata: Metadata<'a>,
message: &'a str,
fields: &'a [Field<'a>],
context: &'a [Field<'a>],
) -> Self {
Self {
metadata,
message,
fields,
context,
}
}
pub fn all_fields(&self) -> impl Iterator<Item = &Field<'a>> {
self.context.iter().chain(self.fields.iter())
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
#[test]
fn field_new_accepts_typed_values() {
let f = Field::new("port", 8080_u32);
assert_eq!(f.key, "port");
assert_eq!(f.value, Value::U64(8080));
}
#[test]
fn metadata_chain_setters() {
let m = Metadata::new(Level::Info, "tgt")
.with_location("file.rs", 12)
.with_timestamp(1_700_000_000_000_000_000);
assert_eq!(m.file, Some("file.rs"));
assert_eq!(m.line, Some(12));
assert_eq!(m.timestamp_unix_nanos, Some(1_700_000_000_000_000_000));
}
#[test]
fn record_all_fields_orders_context_first() {
let ctx = [Field::new("trace_id", "abc")];
let fields = [Field::new("port", 80_u32)];
let record = Record::with_context(Metadata::new(Level::Info, "tgt"), "msg", &fields, &ctx);
let keys: Vec<_> = record.all_fields().map(|f| f.key).collect();
assert_eq!(keys, vec!["trace_id", "port"]);
}
}