use std::fmt::Write as _;
use crate::format::FormatConfig;
use crate::visit::FieldMap;
#[derive(Debug, Clone)]
pub(crate) struct SpanLink {
pub name: &'static str,
pub fields: FieldMap,
}
pub(crate) fn render_span_name(cfg: &FormatConfig, link: &SpanLink) -> String {
let mut out = String::with_capacity(link.name.len() + 2);
out.push_str(link.name);
out.push_str(&cfg.function_bracket_left);
let mut first = true;
for (name, value) in &link.fields {
if !first {
out.push_str(&cfg.arguments_separator);
}
first = false;
let _ = write!(out, "{}{}{}", name, cfg.arguments_equality, value);
}
out.push_str(&cfg.function_bracket_right);
out
}
pub(crate) fn render_chain(
cfg: &FormatConfig,
target: &str,
parents: &[SpanLink],
) -> String {
let mut chain = String::new();
if cfg.show_target {
chain.push_str(target);
chain.push_str(&cfg.span_separator);
}
for link in parents {
chain.push_str(&render_span_name(cfg, link));
chain.push_str(&cfg.span_separator);
}
chain
}
#[cfg(test)]
mod tests {
use super::*;
use crate::visit::FieldValue;
use std::collections::BTreeMap;
fn link(name: &'static str, kvs: &[(&'static str, FieldValue)]) -> SpanLink {
let mut m = BTreeMap::new();
for (k, v) in kvs {
m.insert(*k, v.clone());
}
SpanLink {
name,
fields: m,
}
}
#[test]
fn span_name_without_fields() {
let cfg = FormatConfig::default();
assert_eq!(render_span_name(&cfg, &link("foo", &[])), "foo()");
}
#[test]
fn span_name_with_fields_uses_separators() {
let cfg = FormatConfig::default();
let l = link(
"foo",
&[
("a", FieldValue::I64(1)),
("b", FieldValue::Str("two".into())),
],
);
assert_eq!(render_span_name(&cfg, &l), "foo(a: 1, b: two)");
}
#[test]
fn chain_joins_parents_with_separator() {
let cfg = FormatConfig::default();
let chain = render_chain(
&cfg,
"irrelevant",
&[link("outer", &[]), link("middle", &[("x", FieldValue::Bool(true))])],
);
assert_eq!(chain, "outer()::middle(x: true)::");
}
#[test]
fn chain_includes_target_when_enabled() {
let cfg = FormatConfig {
show_target: true,
..FormatConfig::default()
};
let chain = render_chain(&cfg, "my_target", &[link("outer", &[])]);
assert_eq!(chain, "my_target::outer()::");
}
#[test]
fn chain_with_no_parents_and_no_target_is_empty() {
let cfg = FormatConfig::default();
assert_eq!(render_chain(&cfg, "ignored", &[]), "");
}
#[test]
fn custom_brackets_and_separator() {
let cfg = FormatConfig {
function_bracket_left: "[".into(),
function_bracket_right: "]".into(),
arguments_equality: "=".into(),
..FormatConfig::default()
};
let l = link("fn", &[("k", FieldValue::U64(7))]);
assert_eq!(render_span_name(&cfg, &l), "fn[k=7]");
}
}