lightning_signer/util/
log_utils.rs

1use crate::prelude::*;
2use anyhow::{anyhow, Result};
3
4const LOG_LEVEL_FILTERS: [log::LevelFilter; 6] = [
5    log::LevelFilter::Off,
6    log::LevelFilter::Error,
7    log::LevelFilter::Warn,
8    log::LevelFilter::Info,
9    log::LevelFilter::Debug,
10    log::LevelFilter::Trace,
11];
12
13/// Name for each log level
14pub const LOG_LEVEL_FILTER_NAMES: [&'static str; 6] =
15    ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"];
16
17/// Parse a log level name to a Level filter
18pub fn parse_log_level_filter(lvlstr: String) -> Result<log::LevelFilter> {
19    Ok(*LOG_LEVEL_FILTERS
20        .iter()
21        .find(|ll| lvlstr == ll.as_str())
22        .ok_or_else(|| anyhow!("invalid log level: {}", lvlstr))?)
23}
24
25/// Macro to catch panics for an expression evaluation and convert them to
26/// Status::internal errors with the given format string and additional args.
27/// The format string must have a {} placeholder for the panic message.
28/// The surrounding function must return a Result<_, Status>.
29#[macro_export]
30macro_rules! catch_panic {
31    ($e:expr, $fmt:expr) => {{
32        catch_panic!($fmt,)
33    }};
34    ($e:expr, $fmt:expr, $($arg:tt)*) => {{
35        #[cfg(feature = "std")]
36        match std::panic::catch_unwind(|| $e) {
37            Ok(res) => res,
38            Err(err) => {
39                let details = if let Some(s) = err.downcast_ref::<String>() {
40                    s.clone()
41                } else if let Some(s) = err.downcast_ref::<&str>() {
42                    s.to_string()
43                } else {
44                    "Unknown panic message".to_string()
45                };
46                ::log::error!($fmt, details, $($arg)*);
47                return Err(crate::util::status::Status::internal(format!($fmt, details, $($arg)*)))
48            }
49        }
50        #[cfg(not(feature = "std"))]
51        $e
52    }};
53}
54
55#[cfg(test)]
56mod tests {
57    use super::parse_log_level_filter;
58    use log::LevelFilter;
59
60    use crate::util::status::{Code, Status};
61
62    #[test]
63    fn catch_test() {
64        fn fut_panic() -> Result<u8, Status> {
65            catch_panic!(panic!("test"), "panic: {} {}", "arg1")
66        }
67        fn fut_success() -> Result<u8, Status> {
68            catch_panic!(Ok(42), "panic: {} {}", "arg1")
69        }
70        let res = fut_panic();
71        assert!(res.is_err());
72        let status = res.unwrap_err();
73        assert_eq!(status.code(), Code::Internal);
74        assert_eq!(status.message(), "panic: test arg1");
75
76        let res = fut_success();
77        assert_eq!(res.unwrap(), 42);
78    }
79
80    #[test]
81    fn test_parse_log_level_filter() {
82        let valid_levels = [
83            ("OFF", LevelFilter::Off),
84            ("ERROR", LevelFilter::Error),
85            ("WARN", LevelFilter::Warn),
86            ("INFO", LevelFilter::Info),
87            ("DEBUG", LevelFilter::Debug),
88            ("TRACE", LevelFilter::Trace),
89        ];
90
91        for (name, expected_filter) in valid_levels.iter() {
92            let result = parse_log_level_filter(name.to_string());
93            assert!(result.is_ok());
94            assert_eq!(result.unwrap(), *expected_filter);
95        }
96
97        // Test an invalid log level name
98        let invalid_level = "INVALID".to_string();
99        let result = parse_log_level_filter(invalid_level);
100        assert!(result.is_err());
101        assert_eq!(result.unwrap_err().to_string(), "invalid log level: INVALID");
102    }
103}