Skip to main content

limen_core/telemetry/
event_message.rs

1//! Strongly-validated, zero-cost event message type.
2//!
3//! This module provides [`EventMessage`], a lightweight wrapper around a
4//! `&'static str` that enforces strict formatting rules **at compile time**.
5//! Messages must not exceed a fixed maximum byte length and must not contain
6//! newline characters. Violations detected in a `const` context produce
7//! compile-time errors.
8//!
9//! The accompanying `event_message!` macro guarantees that all validation
10//! happens during compilation, even when invoked inside non-const functions.
11//! When compiled successfully, an `EventMessage` is represented and used at
12//! runtime with **no overhead**, making it as efficient as a bare `&'static str`.
13//!
14//! This module is intended for log messages, telemetry identifiers, and any
15//! system where event message text must be validated, immutable, and fully
16//! static while still incurring zero runtime cost.
17
18/// A strongly-validated, zero-cost wrapper around a `&'static str` intended for
19/// use in event messages, logs, telemetry, or any system where input strings
20/// must meet strict formatting requirements.
21///
22/// # Overview
23///
24/// `EventMessage` wraps a `&'static str`, but with **compile-time validation**
25/// whenever it is constructed in a `const` context. The validation enforces:
26///
27/// - The message must not exceed [`EventMessage::MAX_LEN`] bytes.
28/// - The message must not contain newline characters (`'\n'` or `'\r'`).
29///
30/// When constructed using the accompanying `event_message!` macro, all
31/// validation is **guaranteed to occur at compile time**, ensuring:
32///
33/// - **Zero runtime cost** (no loops, no checks, no panics).
34/// - **Static enforcement** of message correctness.
35/// - **Identical runtime performance** to using a plain `&'static str`.
36///
37/// # Why use this type?
38///
39/// This type is useful when you need a runtime message type that is:
40///
41/// - Guaranteed to be short enough for logging systems or wire protocols.
42/// - Guaranteed to be newline-free (e.g., single-line logs).
43/// - Immutable and `'static`.
44/// - Just as cheap as storing a `&'static str`.
45///
46/// Once constructed, an `EventMessage` is simply a thin wrapper around a string
47/// slice. Accessing the inner string via [`EventMessage::as_str`] is fully
48/// inlined and has no measurable overhead.
49///
50/// # Compile-time vs runtime construction
51///
52/// - Using `EventMessage::new` inside a `const` context performs validation at
53///   compile time.
54/// - Using `EventMessage::new` at runtime may incur runtime checking cost.
55/// - Using the `event_message!` macro **always** validates at compile time.
56///
57/// Prefer `event_message!` for maximal performance and strictness.
58///
59/// # Examples
60///
61/// ## Compile-time validated constant
62///
63/// ```rust
64/// use limen_core::prelude::event_message::EventMessage;
65///
66/// const HELLO: EventMessage = EventMessage::new("hello");
67/// ```
68///
69/// ## Preferable usage: enforced via `event_message!`
70///
71/// ```rust
72/// use limen_core::prelude::event_message::EventMessage;
73/// use limen_core::event_message;
74///
75/// let msg: EventMessage = event_message!("system_ready");
76/// println!("{}", msg.as_str());
77/// ```
78///
79/// Attempting to use invalid strings results in a **compile-time error**:
80///
81/// ```rust,compile_fail
82/// use limen_core::prelude::event_message::EventMessage;
83///
84/// const BAD: EventMessage = EventMessage::new("line1\nline2"); // contains newline
85/// ```
86///
87/// ```rust,compile_fail
88/// use limen_core::prelude::event_message::EventMessage;
89///
90/// const TOO_LONG: EventMessage = EventMessage::new(
91///     "12345678901234567890123456789012345678901234567890\
92///12345678901234567890123456789012345678901234567890\
93///123456789012345678901234567890"
94/// );
95/// ```
96#[repr(transparent)]
97#[derive(Copy, Clone, Debug)]
98pub struct EventMessage(&'static str);
99
100impl EventMessage {
101    /// The maximum allowed message length in bytes.
102    ///
103    /// Messages longer than this value will cause a compile-time error if
104    /// validated in a `const` context, or a panic if validated at runtime.
105    pub const MAX_LEN: usize = 128;
106
107    /// Creates a new `EventMessage` from a `&'static str`, performing
108    /// full validation when invoked in a `const` context.
109    ///
110    /// # Validation
111    ///
112    /// This function checks:
113    ///
114    /// - The string's length does not exceed [`EventMessage::MAX_LEN`].
115    /// - The string contains no newline characters (`'\n'` or `'\r'`).
116    ///
117    /// # Compile-Time Guarantees
118    ///
119    /// When invoked in a `const` context:
120    ///
121    /// - Validation happens during compilation.
122    /// - Violations cause a compile-time error.
123    /// - No runtime code is emitted for the check.
124    ///
125    /// # Runtime Behavior
126    ///
127    /// If invoked at runtime:
128    ///
129    /// - Validation executes at runtime.
130    /// - Violations cause a runtime panic.
131    ///
132    /// For most usage, the `event_message!` macro ensures compile-time
133    /// validation in all cases.
134    pub const fn new(s: &'static str) -> Self {
135        let bytes = s.as_bytes();
136        let len = bytes.len();
137
138        if len > Self::MAX_LEN {
139            panic!("EventMessage is too long");
140        }
141
142        let mut index = 0;
143        while index < len {
144            let byte = bytes[index];
145            if byte == b'\n' || byte == b'\r' {
146                panic!("EventMessage contains newline characters");
147            }
148            index += 1;
149        }
150
151        Self(s)
152    }
153
154    /// Returns the inner `&'static str`.
155    ///
156    /// This method is `const` and `#[inline]`, and therefore has no runtime
157    /// overhead. It is equivalent in performance to using a plain
158    /// `&'static str` directly.
159    #[inline]
160    pub const fn as_str(&self) -> &'static str {
161        self.0
162    }
163}
164
165/// Constructs an [`EventMessage`] with **guaranteed compile-time validation**.
166///
167/// This macro ensures that:
168///
169/// - The message is validated in a `const` context.
170/// - No runtime checks are ever emitted.
171/// - The resulting value is as cheap as a `&'static str`.
172///
173/// # Why this macro is recommended
174///
175/// Even inside non-const functions, Rust evaluates the validation inside a
176/// compile-time `const` item. This makes the macro strictly superior to calling
177/// [`EventMessage::new`] directly at runtime.
178///
179/// # Examples
180///
181/// ```rust
182/// use limen_core::prelude::event_message::EventMessage;
183/// use limen_core::event_message;
184///
185/// let msg = event_message!("startup_complete");
186/// println!("{}", msg.as_str());
187/// ```
188///
189/// Invalid messages cause **compile-time errors**:
190///
191/// ```rust,compile_fail
192/// use limen_core::prelude::event_message::EventMessage;
193/// use limen_core::event_message;
194///
195/// let bad = event_message!("line1\nline2");
196/// ```
197///
198/// ```rust,compile_fail
199/// use limen_core::prelude::event_message::EventMessage;
200/// use limen_core::event_message;
201///
202/// let too_long = event_message!(
203///     "12345678901234567890123456789012345678901234567890\
204///12345678901234567890123456789012345678901234567890\
205///123456789012345678901234567890"
206/// );
207/// ```
208#[macro_export]
209macro_rules! event_message {
210    ($s:expr) => {{
211        const MSG: $crate::telemetry::event_message::EventMessage =
212            $crate::telemetry::event_message::EventMessage::new($s);
213        MSG
214    }};
215}