1#![no_std]
2
3use core::{
4 net::{IpAddr, Ipv4Addr, Ipv6Addr},
5 num::TryFromIntError,
6};
7
8use num_enum::IntoPrimitive;
9
10pub type LogValueLength = u16;
11
12#[repr(u8)]
13#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, IntoPrimitive)]
14pub enum Level {
15 Error = 1,
19 Warn,
23 Info,
27 Debug,
31 Trace,
35}
36
37macro_rules! impl_formatter_for_types {
38 ($trait:path : { $($type:ty),*}) => {
39 $(
40 impl $trait for $type {}
41 )*
42 };
43}
44
45pub trait DefaultFormatter {}
46impl_formatter_for_types!(
47 DefaultFormatter: {
48 bool,
49 i8, i16, i32, i64, isize,
50 u8, u16, u32, u64, usize,
51 f32, f64,
52 char,
53 str,
54 &str,
55 IpAddr, Ipv4Addr, Ipv6Addr
56 }
57);
58
59pub trait LowerHexFormatter {}
60impl_formatter_for_types!(
61 LowerHexFormatter: {
62 i8, i16, i32, i64, isize,
63 u8, u16, u32, u64, usize,
64 &[u8]
65 }
66);
67
68impl<const N: usize> LowerHexFormatter for &[u8; N] {}
69
70pub trait UpperHexFormatter {}
71impl_formatter_for_types!(
72 UpperHexFormatter: {
73 i8, i16, i32, i64, isize,
74 u8, u16, u32, u64, usize,
75 &[u8]
76 }
77);
78
79impl<const N: usize> UpperHexFormatter for &[u8; N] {}
80
81pub trait IpFormatter {}
82impl IpFormatter for IpAddr {}
83impl IpFormatter for Ipv4Addr {}
84impl IpFormatter for Ipv6Addr {}
85impl IpFormatter for u32 {}
86impl IpFormatter for [u8; 4] {}
87impl IpFormatter for [u8; 16] {}
88impl IpFormatter for [u16; 8] {}
89
90pub trait LowerMacFormatter {}
91impl LowerMacFormatter for [u8; 6] {}
92
93pub trait UpperMacFormatter {}
94impl UpperMacFormatter for [u8; 6] {}
95
96pub trait PointerFormatter {}
97impl<T> PointerFormatter for *const T {}
98impl<T> PointerFormatter for *mut T {}
99
100#[repr(u8)]
101#[derive(Copy, Clone, Debug, IntoPrimitive)]
102pub enum RecordFieldKind {
103 Target,
104 Level,
105 Module,
106 File,
107 Line,
108 NumArgs,
109}
110
111#[repr(u8)]
114#[derive(Copy, Clone, Debug, IntoPrimitive)]
115pub enum ArgumentKind {
116 DisplayHint,
117
118 I8,
119 I16,
120 I32,
121 I64,
122 Isize,
123
124 U8,
125 U16,
126 U32,
127 U64,
128 Usize,
129
130 F32,
131 F64,
132
133 Ipv4Addr,
134 Ipv6Addr,
135
136 ArrU8Len4,
138 ArrU8Len6,
140 ArrU8Len16,
142 ArrU16Len8,
144
145 Bytes,
146 Str,
147
148 Pointer,
149}
150
151#[repr(u8)]
153#[derive(Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive)]
154pub enum DisplayHint {
155 Default = 1,
157 LowerHex,
159 UpperHex,
161 Ip,
163 LowerMac,
165 UpperMac,
167 Pointer,
169}
170
171mod sealed {
172 #[expect(unnameable_types, reason = "this is the sealed trait pattern")]
173 pub trait Sealed {}
174}
175
176pub trait Argument: sealed::Sealed {
177 fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>);
178}
179
180macro_rules! impl_argument {
181 ($self:ident, $arg_type:expr) => {
182 impl sealed::Sealed for $self {}
183
184 impl Argument for $self {
185 fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
186 ($arg_type, self.to_ne_bytes())
187 }
188 }
189 };
190}
191
192impl_argument!(i8, ArgumentKind::I8);
193impl_argument!(i16, ArgumentKind::I16);
194impl_argument!(i32, ArgumentKind::I32);
195impl_argument!(i64, ArgumentKind::I64);
196impl_argument!(isize, ArgumentKind::Isize);
197
198impl_argument!(u8, ArgumentKind::U8);
199impl_argument!(u16, ArgumentKind::U16);
200impl_argument!(u32, ArgumentKind::U32);
201impl_argument!(u64, ArgumentKind::U64);
202impl_argument!(usize, ArgumentKind::Usize);
203
204impl_argument!(f32, ArgumentKind::F32);
205impl_argument!(f64, ArgumentKind::F64);
206
207enum Either<L, R> {
208 Left(L),
209 Right(R),
210}
211
212impl<L, R> AsRef<[u8]> for Either<L, R>
213where
214 L: AsRef<[u8]>,
215 R: AsRef<[u8]>,
216{
217 fn as_ref(&self) -> &[u8] {
218 match self {
219 Self::Left(l) => l.as_ref(),
220 Self::Right(r) => r.as_ref(),
221 }
222 }
223}
224
225impl sealed::Sealed for IpAddr {}
226impl Argument for IpAddr {
227 fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
228 match self {
229 Self::V4(ipv4_addr) => {
230 let (kind, value) = ipv4_addr.as_argument();
231 (kind, Either::Left(value))
232 }
233 Self::V6(ipv6_addr) => {
234 let (kind, value) = ipv6_addr.as_argument();
235 (kind, Either::Right(value))
236 }
237 }
238 }
239}
240
241impl sealed::Sealed for Ipv4Addr {}
242impl Argument for Ipv4Addr {
243 fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
244 (ArgumentKind::Ipv4Addr, self.octets())
245 }
246}
247
248impl sealed::Sealed for Ipv6Addr {}
249impl Argument for Ipv6Addr {
250 fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
251 (ArgumentKind::Ipv6Addr, self.octets())
252 }
253}
254
255impl<const N: usize> sealed::Sealed for [u8; N] {}
256impl<const N: usize> Argument for [u8; N] {
257 fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
258 let kind = match N {
259 4 => ArgumentKind::ArrU8Len4,
260 6 => ArgumentKind::ArrU8Len6,
261 16 => ArgumentKind::ArrU8Len16,
262 _ => ArgumentKind::Bytes,
263 };
264 (kind, *self)
265 }
266}
267
268impl sealed::Sealed for &[u8] {}
269impl Argument for &[u8] {
270 fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
271 (ArgumentKind::Bytes, *self)
272 }
273}
274
275impl sealed::Sealed for &str {}
276impl Argument for &str {
277 fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
278 (ArgumentKind::Str, self.as_bytes())
279 }
280}
281
282impl sealed::Sealed for DisplayHint {}
283impl Argument for DisplayHint {
284 fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
285 let v: u8 = (*self).into();
286 (ArgumentKind::DisplayHint, v.to_ne_bytes())
287 }
288}
289
290impl<T> sealed::Sealed for *const T {}
291impl<T> Argument for *const T {
292 fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
293 (ArgumentKind::Pointer, (*self as usize).to_ne_bytes())
294 }
295}
296
297impl<T> sealed::Sealed for *mut T {}
298impl<T> Argument for *mut T {
299 fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
300 (ArgumentKind::Pointer, (*self as usize).to_ne_bytes())
301 }
302}
303
304fn wire_len(value: &[u8]) -> Option<[u8; 2]> {
305 match LogValueLength::try_from(value.len()) {
306 Ok(wire_len) => Some(wire_len.to_ne_bytes()),
307 Err(TryFromIntError { .. }) => None,
308 }
309}
310
311#[doc(hidden)]
312pub struct Field<T>([u8; 1], [u8; 2], T);
313
314impl<T: AsRef<[u8]>> Field<T> {
315 pub fn new(kind: impl Into<u8>, value: T) -> Option<Self> {
316 let wire_len = wire_len(value.as_ref())?;
317 Some(Self([kind.into()], wire_len, value))
318 }
319
320 pub fn with_bytes(&self, op: &mut impl FnMut(&[u8]) -> Option<()>) -> Option<()> {
321 let Self(kind, wire_len, value) = self;
322 op(&kind[..])?;
323 op(&wire_len[..])?;
324 op(value.as_ref())?;
325 Some(())
326 }
327}
328
329#[doc(hidden)]
330pub struct Header<'a> {
331 target: Field<&'a [u8]>,
332 level: Field<[u8; 1]>,
333 module: Field<&'a [u8]>,
334 file: Field<&'a [u8]>,
335 line: Field<[u8; 4]>,
336 num_args: Field<[u8; 4]>,
337}
338
339impl<'a> Header<'a> {
340 pub fn new(
341 target: &'a str,
342 level: Level,
343 module: &'a str,
344 file: &'a str,
345 line: u32,
346 num_args: u32,
347 ) -> Option<Self> {
348 let target = target.as_bytes();
349 let level: u8 = level.into();
350 let level = level.to_ne_bytes();
351 let module = module.as_bytes();
352 let file = file.as_bytes();
353 let line = line.to_ne_bytes();
354 let num_args = num_args.to_ne_bytes();
355 let target = Field::new(RecordFieldKind::Target, target)?;
356 let level = Field::new(RecordFieldKind::Level, level)?;
357 let module = Field::new(RecordFieldKind::Module, module)?;
358 let file = Field::new(RecordFieldKind::File, file)?;
359 let line = Field::new(RecordFieldKind::Line, line)?;
360 let num_args = Field::new(RecordFieldKind::NumArgs, num_args)?;
361
362 Some(Self {
363 target,
364 level,
365 module,
366 file,
367 line,
368 num_args,
369 })
370 }
371
372 pub fn with_bytes(&self, op: &mut impl FnMut(&[u8]) -> Option<()>) -> Option<()> {
373 let Self {
374 target,
375 level,
376 module,
377 file,
378 line,
379 num_args,
380 } = self;
381 target.with_bytes(op)?;
382 level.with_bytes(op)?;
383 module.with_bytes(op)?;
384 file.with_bytes(op)?;
385 line.with_bytes(op)?;
386 num_args.with_bytes(op)?;
387 Some(())
388 }
389}