Skip to main content

state_engine/common/
log_format.rs

1use serde_json::Value;
2
3/// # Examples
4/// ```
5/// use state_engine::common::LogFormat;
6///
7/// let fn_message = LogFormat::call("State", "get", &["'key'".to_string()]);
8/// assert_eq!(fn_message, "State::get('key')");
9/// ```
10pub struct LogFormat;
11
12impl LogFormat {
13    pub fn call(class: &str, fn_name: &str, args: &[String]) -> String {
14        let args_str = args.join(", ");
15        format!("{}::{}({})", class, fn_name, args_str)
16    }
17
18    /// Format JSON value for log output
19    ///
20    /// # Examples
21    /// ```
22    /// use state_engine::common::LogFormat;
23    /// use serde_json::json;
24    ///
25    /// assert_eq!(LogFormat::format_arg(&json!("text")), "'text'");
26    /// assert_eq!(LogFormat::format_arg(&json!(42)), "42");
27    /// assert_eq!(LogFormat::format_arg(&json!(true)), "true");
28    /// assert_eq!(LogFormat::format_arg(&json!(null)), "null");
29    /// assert_eq!(LogFormat::format_arg(&json!([])), "[]");
30    /// assert_eq!(LogFormat::format_arg(&json!({})), "{}");
31    /// assert_eq!(LogFormat::format_arg(&json!([1, 2, 3])), "[3 items]");
32    /// assert_eq!(LogFormat::format_arg(&json!({"a": 1})), "{1 fields}");
33    /// ```
34    pub fn format_arg(value: &Value) -> String {
35        match value {
36            Value::String(s) if s.len() > 50 => format!("'{}'...", &s[..47]),
37            Value::String(s) => format!("'{}'", s),
38            Value::Array(arr) if arr.is_empty() => "[]".to_string(),
39            Value::Array(arr) => format!("[{} items]", arr.len()),
40            Value::Object(obj) if obj.is_empty() => "{}".to_string(),
41            Value::Object(obj) => format!("{{{} fields}}", obj.len()),
42            Value::Null => "null".to_string(),
43            Value::Bool(b) => b.to_string(),
44            Value::Number(n) => n.to_string(),
45        }
46    }
47
48    /// Format string argument for log output
49    ///
50    /// # Examples
51    /// ```
52    /// use state_engine::common::LogFormat;
53    ///
54    /// assert_eq!(LogFormat::format_str_arg("key"), "'key'");
55    /// ```
56    pub fn format_str_arg(s: &str) -> String {
57        if s.len() > 50 {
58            format!("'{}'...", &s[..47])
59        } else {
60            format!("'{}'", s)
61        }
62    }
63}
64
65/// Log macro: fn call
66///
67/// # Examples
68/// ```ignore
69/// use crate::fn_log;
70///
71/// fn_log!("State", "get", "cache.user");
72/// // Logs: State::get('cache.user')
73/// ```
74#[macro_export]
75macro_rules! fn_log {
76    ($class:expr, $fun:expr $(, $arg:expr)*) => {{
77        #[cfg(feature = "logging")]
78        {
79            let args: Vec<String> = vec![
80                $(
81                    $crate::common::LogFormat::format_str_arg($arg),
82                )*
83            ];
84            log::debug!("{}", $crate::common::LogFormat::call($class, $fun, &args));
85        }
86    }};
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use serde_json::json;
93
94    #[test]
95    fn test_fn_multiple_args() {
96        let result = LogFormat::call("State", "get", &[
97            "'cache.user'".to_string(),
98            "null".to_string(),
99        ]);
100        assert_eq!(result, "State::get('cache.user', null)");
101    }
102
103    #[test]
104    fn test_format_arg_long_string() {
105        let long_str = "a".repeat(60);
106        let result = LogFormat::format_arg(&json!(long_str));
107        assert!(result.starts_with("'aaa"));
108        assert!(result.ends_with("'..."));
109        assert_eq!(result.len(), 52);
110    }
111
112    #[test]
113    fn test_format_str_arg_long_string() {
114        let long_str = "a".repeat(60);
115        let result = LogFormat::format_str_arg(&long_str);
116        assert!(result.starts_with("'aaa"));
117        assert!(result.ends_with("'..."));
118    }
119}