diff --git a/aimdb-core/Cargo.toml b/aimdb-core/Cargo.toml index 6b2da2c6..c664f375 100644 --- a/aimdb-core/Cargo.toml +++ b/aimdb-core/Cargo.toml @@ -52,7 +52,7 @@ connector-session = ["alloc"] # Observability features (available on both std/no_std) tracing = ["dep:tracing"] # Works in both std and no_std environments -defmt = ["dep:defmt"] # Embedded logging via probe (no_std) +defmt = ["dep:defmt", "defmt/alloc"] # Embedded logging via probe (no_std) # A second facade destination, for hosts that cannot be handed a global # `tracing` subscriber (design 050). Not in `default`: 66 call sites on an MCU # should not each grow a level check because an FFI layer wanted one. diff --git a/aimdb-core/src/error.rs b/aimdb-core/src/error.rs index c6d23747..6d2bdf9b 100644 --- a/aimdb-core/src/error.rs +++ b/aimdb-core/src/error.rs @@ -223,6 +223,16 @@ pub enum DbError { }, } +// Through `Display` (`thiserror`), not a derive: the `std`-only variants carry +// `io::Error` / `serde_json::Error`, which do not implement `defmt::Format`, +// and docs.rs builds with every feature on, `std` and `defmt` together. +#[cfg(feature = "defmt")] +impl defmt::Format for DbError { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "{}", defmt::Display2Format(self)) + } +} + // ===== DbError Implementation ===== impl DbError { /// Builds a [`RuntimeError`](DbError::RuntimeError). diff --git a/aimdb-core/src/lib.rs b/aimdb-core/src/lib.rs index acab7040..b770c5fe 100644 --- a/aimdb-core/src/lib.rs +++ b/aimdb-core/src/lib.rs @@ -75,6 +75,22 @@ pub mod __private { pub use ::tracing; } +// Every type the `log_*!` macros format must implement `defmt::Format` when +// `defmt` is on (#352); this fails the build if one stops doing so. +#[cfg(all(feature = "defmt", feature = "connector-session"))] +const _: () = { + fn format() {} + let _ = format::; + let _ = format::; + let _ = format::; + let _ = format::; + let _ = format::; + let _ = format::; + let _ = format::; + let _ = format::>; + let _ = format::>; +}; + pub mod buffer; pub mod builder; #[cfg(feature = "remote")] diff --git a/aimdb-core/src/outbound/routes.rs b/aimdb-core/src/outbound/routes.rs index 2d2644e3..d598cd35 100644 --- a/aimdb-core/src/outbound/routes.rs +++ b/aimdb-core/src/outbound/routes.rs @@ -126,6 +126,15 @@ pub(crate) enum SerializeFailure { InvalidLength { len: usize, capacity: usize }, } +// Through `Display`: the variants carry a `SerializeError` and lengths, and +// one rendering keeps the defmt line identical to the tracing/log one. +#[cfg(feature = "defmt")] +impl defmt::Format for SerializeFailure { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "{}", defmt::Display2Format(self)) + } +} + impl core::fmt::Display for SerializeFailure { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { diff --git a/aimdb-core/src/session/io.rs b/aimdb-core/src/session/io.rs index c7344c80..0fd5b4f1 100644 --- a/aimdb-core/src/session/io.rs +++ b/aimdb-core/src/session/io.rs @@ -196,6 +196,7 @@ pub trait Delay { /// none, so nothing tells payload bytes from the next header. Only the framer /// knows which case it is in, so it says, rather than the connection guessing. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FrameFault { /// Bad frame, good link: skip it and keep reading. Recoverable, diff --git a/aimdb-core/src/session/mod.rs b/aimdb-core/src/session/mod.rs index 79a8caac..eaa25c0a 100644 --- a/aimdb-core/src/session/mod.rs +++ b/aimdb-core/src/session/mod.rs @@ -224,6 +224,7 @@ impl Default for SessionLimits { /// Transport-layer failure (Layer 1). #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum TransportError { /// The connection was closed or reset by the peer. @@ -239,6 +240,7 @@ pub enum TransportError { /// Envelope-codec failure — a frame could not be decoded/encoded. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum CodecError { /// The frame was not valid for this envelope format. @@ -262,6 +264,7 @@ pub enum RpcError { /// Authentication failure raised by [`Dispatch::authenticate`]. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum AuthError { /// Credentials were missing or rejected.