Skip to main content

context_engine/
debug_log.rs

1#[cfg(feature = "logging")]
2use alloc::string::{String, ToString};
3#[cfg(feature = "logging")]
4use crate::provided::Tree;
5
6#[cfg(feature = "logging")]
7pub fn message(class: &str, fn_name: &str, args: &[&str]) -> String {
8    let mut s = String::from(class);
9    s.push_str("::");
10    s.push_str(fn_name);
11    s.push('(');
12    for (i, arg) in args.iter().enumerate() {
13        if i > 0 { s.push_str(", "); }
14        s.push_str(arg);
15    }
16    s.push(')');
17    s
18}
19
20#[cfg(feature = "logging")]
21pub fn format_arg(value: &Tree) -> String {
22    match value {
23        Tree::Scalar(b) => {
24            let s = String::from_utf8_lossy(b);
25            if s.len() > 50 {
26                let mut out = String::from("'");
27                out.push_str(&s[..47]);
28                out.push_str("'...");
29                out
30            } else {
31                let mut out = String::from("'");
32                out.push_str(&s);
33                out.push('\'');
34                out
35            }
36        }
37        Tree::Sequence(arr) if arr.is_empty() => String::from("[]"),
38        Tree::Sequence(arr) => {
39            let mut s = String::from("[");
40            s.push_str(&arr.len().to_string());
41            s.push_str(" items]");
42            s
43        }
44        Tree::Mapping(obj) if obj.is_empty() => String::from("{}"),
45        Tree::Mapping(obj) => {
46            let mut s = String::from("{");
47            s.push_str(&obj.len().to_string());
48            s.push_str(" fields}");
49            s
50        }
51        Tree::Null => String::from("null"),
52    }
53}
54
55#[cfg(feature = "logging")]
56pub fn format_str_arg(s: &str) -> String {
57    if s.len() > 50 {
58        let mut out = String::from("'");
59        out.push_str(&s[..47]);
60        out.push_str("'...");
61        out
62    } else {
63        let mut out = String::from("'");
64        out.push_str(s);
65        out.push('\'');
66        out
67    }
68}
69
70#[macro_export]
71macro_rules! debug_log {
72    ($class:expr, $fun:expr $(, $arg:expr)*) => {{
73        #[cfg(feature = "logging")]
74        {
75            let formatted: $crate::Vec<$crate::String> = $crate::vec![
76                $( $crate::debug_log::format_str_arg($arg), )*
77            ];
78            let refs: $crate::Vec<&str> = formatted.iter().map(|s| s.as_str()).collect();
79            log::debug!("{}", $crate::debug_log::message($class, $fun, &refs));
80        }
81    }};
82}
83
84#[cfg(all(test, feature = "logging"))]
85mod tests {
86    use super::*;
87    use alloc::vec;
88
89    #[test]
90    fn message_multiple_args() {
91        let result = message("State", "get", &["'cache.user'", "null"]);
92        assert_eq!(result, "State::get('cache.user', null)");
93    }
94
95    #[test]
96    fn format_arg_variants() {
97        assert_eq!(format_arg(&Tree::Scalar(b"text".to_vec())), "'text'");
98        assert_eq!(format_arg(&Tree::Null), "null");
99        assert_eq!(format_arg(&Tree::Sequence(vec![])), "[]");
100        assert_eq!(format_arg(&Tree::Mapping(vec![])), "{}");
101        assert_eq!(format_arg(&Tree::Sequence(vec![Tree::Null, Tree::Null, Tree::Null])), "[3 items]");
102        assert_eq!(format_arg(&Tree::Mapping(vec![(b"a".to_vec(), Tree::Null)])), "{1 fields}");
103    }
104
105    #[test]
106    fn format_arg_long_string_truncated() {
107        let long_str = "a".repeat(60);
108        let result = format_arg(&Tree::Scalar(long_str.into_bytes()));
109        assert!(result.starts_with("'aaa"));
110        assert!(result.ends_with("'..."));
111        assert_eq!(result.len(), 52);
112    }
113
114    #[test]
115    fn format_str_arg_short_and_long() {
116        assert_eq!(format_str_arg("key"), "'key'");
117        let long_str = "a".repeat(60);
118        let result = format_str_arg(&long_str);
119        assert!(result.starts_with("'aaa"));
120        assert!(result.ends_with("'..."));
121    }
122}