1use serde_json::{Map, Value};
6
7pub mod keys {
9 pub const SESSION_ID: &str = "session_id";
11 pub const USER_ID: &str = "user_id";
13 pub const RELEASE: &str = "release";
15 pub const VERSION: &str = "version";
17 pub const ENVIRONMENT: &str = "environment";
19 pub const PUBLIC: &str = "public";
21}
22
23pub struct TraceMeta;
25
26impl TraceMeta {
27 pub fn session(id: impl Into<String>) -> (&'static str, Value) {
29 (keys::SESSION_ID, Value::String(id.into()))
30 }
31
32 pub fn user(id: impl Into<String>) -> (&'static str, Value) {
34 (keys::USER_ID, Value::String(id.into()))
35 }
36
37 pub fn release(s: impl Into<String>) -> (&'static str, Value) {
39 (keys::RELEASE, Value::String(s.into()))
40 }
41
42 pub fn environment(s: impl Into<String>) -> (&'static str, Value) {
44 (keys::ENVIRONMENT, Value::String(s.into()))
45 }
46
47 pub fn version(s: impl Into<String>) -> (&'static str, Value) {
49 (keys::VERSION, Value::String(s.into()))
50 }
51
52 pub fn public(b: bool) -> (&'static str, Value) {
54 (keys::PUBLIC, Value::Bool(b))
55 }
56}
57
58pub fn merge_into(metadata: Value, kv: (&str, Value)) -> Value {
61 let mut map = match metadata {
62 Value::Object(m) => m,
63 _ => Map::new(),
64 };
65 map.insert(kv.0.to_string(), kv.1);
66 Value::Object(map)
67}
68
69pub fn read_string(metadata: &Value, key: &str) -> Option<String> {
71 metadata.as_object()?.get(key)?.as_str().map(str::to_owned)
72}
73
74pub fn read_bool(metadata: &Value, key: &str) -> Option<bool> {
76 metadata.as_object()?.get(key)?.as_bool()
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn merge_into_null_creates_object() {
85 let v = merge_into(Value::Null, TraceMeta::session("s1"));
86 assert_eq!(v.as_object().unwrap()["session_id"], "s1");
87 }
88
89 #[test]
90 fn merge_into_existing_object_inserts_key() {
91 let v = merge_into(serde_json::json!({"a": 1}), TraceMeta::user("u1"));
92 let obj = v.as_object().unwrap();
93 assert_eq!(obj["a"], 1);
94 assert_eq!(obj["user_id"], "u1");
95 }
96
97 #[test]
98 fn merge_into_overwrites_same_key() {
99 let v = merge_into(Value::Null, TraceMeta::session("first"));
100 let v = merge_into(v, TraceMeta::session("second"));
101 assert_eq!(v.as_object().unwrap()["session_id"], "second");
102 }
103
104 #[test]
105 fn read_string_returns_none_when_missing() {
106 assert_eq!(read_string(&Value::Null, keys::SESSION_ID), None);
107 assert_eq!(
108 read_string(&serde_json::json!({"a": 1}), keys::SESSION_ID),
109 None
110 );
111 }
112
113 #[test]
114 fn read_string_finds_well_known_key() {
115 let v = merge_into(Value::Null, TraceMeta::release("v1.2.3"));
116 assert_eq!(read_string(&v, keys::RELEASE).as_deref(), Some("v1.2.3"));
117 }
118
119 #[test]
120 fn read_bool_finds_public() {
121 let v = merge_into(Value::Null, TraceMeta::public(true));
122 assert_eq!(read_bool(&v, keys::PUBLIC), Some(true));
123 }
124}