1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! Integration with [log](https://docs.rs/crate/log)
//!
//! As embedded development implies `#[no_std]` we cannot box
//! our logger so instead simplest approach would be to declare our logger static
//!
//! ```rust
//! use log::info;
//! use cortex_m_log::log::{Logger, init};
//! use cortex_m_log::printer::Dummy;
//!
//! static LOGGER: Logger<Dummy> = Logger {
//!     inner: Dummy::new(),
//!     level: log::LevelFilter::Off
//! };
//!
//! fn main() {
//!     init(&LOGGER).expect("To set logger");
//!
//!     info!("Starting my cute program");
//! }
//! ```
//!
//! Of course since there is no `const-fn` and some printers require
//! Initialization function we need some better way.
//!
//! One way to do it would be to trick compiler by changing lifetime of stack
//! allocated logger to static, obviously unsafe.
//!
//! ```rust,no_run
//! use core::mem;
//!
//! use cortex_m_log::log::{Logger, trick_init};
//! use cortex_m_log::printer::semihosting;
//! use cortex_m_log::modes::InterruptOk;
//!
//! let logger = Logger {
//!     //Uses semihosting as destination with no interrupt control.
//!     inner: semihosting::InterruptOk::<_>::stdout().expect("Get Semihosting stdout"),
//!     level: log::LevelFilter::Info
//! };
//! //Haha trust me, it is safe ;)
//! //As long as logger is not dropped....
//! unsafe {
//!     let _ = trick_init(&logger);
//! }
//! ```
//!
//! Obviously it is UB to drop logger after that and use any of log's macros
use core::mem;
use core::marker;
use crate::printer::Printer;

///Simple Logger implementation
pub struct Logger<P: Printer + marker::Send + marker::Sync> {
    ///Printer implementation
    pub inner: P,
    ///Log level
    pub level: log::LevelFilter,
}

impl<P: Printer + marker::Send + marker::Sync> log::Log for Logger<P> {
    fn enabled(&self, metadata: &log::Metadata) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &log::Record) {
        if self.enabled(record.metadata()) {
            //Pretend we're the Cell
            let inner = &self.inner as *const P as *mut P;
            let inner = unsafe { &mut *inner };

            inner.print(format_args!("{:<5} {}:{} - {}\n", record.level(), record.file().unwrap_or("UNKNOWN"), record.line().unwrap_or(0), record.args()))
        }
    }

    fn flush(&self) {
    }
}

///Initialize logging facilities.
///
///Currently lacks a nice way to set global logger so user
///must himself guarantee static lifetime
///
///## Atomic availability notes
///
///On some targets, only limited set of atomic ops are available.
///In this case, this function initializes logger only once
pub fn init<P: Printer + marker::Send + marker::Sync>(logger: &'static Logger<P>) -> Result<(), log::SetLoggerError> {
    log::set_max_level(logger.level);
    #[cfg(feature = "atomic_cas")]
    {
        log::set_logger(logger)
    }
    #[cfg(not(feature = "atomic_cas"))]
    {
        use core::sync::atomic::{Ordering, AtomicBool};
        static INIT: AtomicBool = AtomicBool::new(false);

        let is_init = INIT.load(Ordering::Acquire);
        INIT.store(true, Ordering::Release);

        match is_init {
            true => Ok(()),
            false => {
                unsafe {
                    log::set_logger_racy(logger)
                }
            }
        }
    }
}

#[inline]
///Performs init by tricking compiler into beliving that `&Logger` is static.
///
///This is unsafe and it is up to you to ensure that reference
///will live longer that any attempts to access it
pub unsafe fn trick_init<P: Printer + marker::Send + marker::Sync + 'static>(logger: &Logger<P>) -> Result<(), log::SetLoggerError> {
    let logger: &'static Logger<P> = mem::transmute(logger);
    init(logger)
}