Skip to main content

nros_log/
sinks.rs

1//! Phase 88.4 — `PlatformSink` + helpers.
2//!
3//! The portable facade ships exactly ONE sink: [`PlatformSink`].
4//! It forwards every record to `nros_platform_log_write` (declared
5//! in `nros-platform-cffi` / extended in Phase 88.3). Per-platform
6//! `nros-platform-<rtos>` crates own the actual delivery.
7//!
8//! Apps wanting fan-out (e.g. `Platform + /rosout` or stdout in a
9//! test harness) compose their own `&'static [&dyn LogSink]` and
10//! pass it to [`crate::init`].
11
12use crate::{LogSink, Record};
13
14unsafe extern "C" {
15    /// Per-platform log delivery (Phase 88). Declared in
16    /// `<nros/platform.h>`; implementor lives in each
17    /// `nros-platform-<rtos>` crate.
18    pub fn nros_platform_log_write(
19        severity: u8,
20        name_ptr: *const u8,
21        name_len: usize,
22        msg_ptr: *const u8,
23        msg_len: usize,
24    );
25
26    /// Per-platform log flush. Default no-op on platforms that
27    /// don't buffer.
28    pub fn nros_platform_log_flush();
29}
30
31/// The default sink: forwards to `nros_platform_log_write`.
32///
33/// Zero-sized. Threading + ISR safety inherit from the linked
34/// `nros-platform-<rtos>` impl — see the table in
35/// `docs/roadmap/archived/phase-88-nros-log.md`.
36pub struct PlatformSink;
37
38impl LogSink for PlatformSink {
39    fn log(&self, record: &Record<'_>) {
40        let name = record.logger_name.as_bytes();
41        let msg = record.message.as_bytes();
42        // SAFETY: pointers come from `&str` / `&'a [u8]` references
43        // that outlive the call; lengths match.
44        unsafe {
45            nros_platform_log_write(
46                record.severity.as_u8(),
47                name.as_ptr(),
48                name.len(),
49                msg.as_ptr(),
50                msg.len(),
51            );
52        }
53    }
54
55    fn flush(&self) {
56        // SAFETY: no args, no preconditions.
57        unsafe { nros_platform_log_flush() };
58    }
59}
60
61static PLATFORM_SINK: PlatformSink = PlatformSink;
62
63/// The default sink list: just `&PLATFORM_SINK`.
64///
65/// Pass to [`crate::init`] for the common case:
66///
67/// ```ignore
68/// nros_log::init(nros_log::sinks::default());
69/// ```
70#[must_use]
71pub fn default() -> &'static [&'static dyn LogSink] {
72    static SINKS: &[&dyn LogSink] = &[&PLATFORM_SINK];
73    SINKS
74}