indexbus_typed/
lib.rs

1#![cfg_attr(not(any(feature = "std", test)), no_std)]
2#![deny(unreachable_pub, rust_2018_idioms)]
3#![deny(missing_docs)]
4
5//! Typed structured-messaging adapters over `indexbus-core`.
6//!
7//! The core remains bytes-first; this crate adds a validated envelope + codec-based typing.
8//!
9//! ## Feature flags
10//!
11//! - `std` (default): enables `std::error::Error` and `Display` for [`Error`].
12//! - `alloc`: enables codecs/adapters that require allocation (this crate itself does not
13//!   require allocation for the raw/borrowed paths).
14//!
15//! ## Envelope and partial effects
16//!
17//! Messages are encoded as a v1 header prefix (`indexbus_msg`) followed by codec-defined payload
18//! bytes.
19//!
20//! Receive-side helpers follow `indexbus-core` semantics: if a message is dequeued and decoding
21//! or validation fails, the message is considered consumed. This prevents a single malformed
22//! message from poisoning the queue.
23//!
24//! ## Minimal typed SPSC (custom codec)
25//!
26//! ```
27//! use indexbus_abi::layouts::SharedLayout;
28//! use indexbus_codec::{CodecError, CodecId, DecodeFrom, EncodeInto};
29//! use indexbus_core::{init_shared_layout, split_spsc, SharedLayoutCell};
30//! use indexbus_typed::{meta, IndexbusMsgSpec, MsgMeta, TypedSpscReceiver, TypedSpscSender};
31//!
32//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
33//! struct MyMsg(u32);
34//!
35//! impl IndexbusMsgSpec for MyMsg {
36//!     const META: MsgMeta = meta(1, 7, 1);
37//! }
38//!
39//! #[derive(Clone, Copy)]
40//! struct U32LeCodec;
41//!
42//! impl CodecId for U32LeCodec {
43//!     const CODEC_ID: u8 = 99;
44//! }
45//!
46//! impl EncodeInto<MyMsg> for U32LeCodec {
47//!     fn encode_into(&self, value: &MyMsg, out: &mut [u8]) -> Result<usize, CodecError> {
48//!         if out.len() < 4 {
49//!             return Err(CodecError::BufferTooSmall {
50//!                 required: 4,
51//!                 provided: out.len(),
52//!             });
53//!         }
54//!         out[..4].copy_from_slice(&value.0.to_le_bytes());
55//!         Ok(4)
56//!     }
57//! }
58//!
59//! impl DecodeFrom<MyMsg> for U32LeCodec {
60//!     fn decode_from(&self, input: &[u8]) -> Result<MyMsg, CodecError> {
61//!         if input.len() < 4 {
62//!             return Err(CodecError::InvalidData);
63//!         }
64//!         let mut b = [0u8; 4];
65//!         b.copy_from_slice(&input[..4]);
66//!         Ok(MyMsg(u32::from_le_bytes(b)))
67//!     }
68//! }
69//!
70//! // Allocate the shared layout on the heap (large type).
71//! let mut shared: Box<SharedLayout> = Box::new(unsafe { core::mem::zeroed() });
72//! init_shared_layout(&mut shared);
73//! let cell = SharedLayoutCell::from_mut(&mut shared);
74//! let (tx0, rx0) = split_spsc(cell).unwrap();
75//!
76//! let tx = TypedSpscSender::new(tx0, U32LeCodec);
77//! let rx = TypedSpscReceiver::new(rx0, U32LeCodec);
78//!
79//! tx.publish_msg(&MyMsg(42)).unwrap();
80//! let (_hdr, got) = rx.try_recv_owned_msg::<MyMsg>().unwrap().unwrap();
81//! assert_eq!(got, MyMsg(42));
82//! ```
83
84#[cfg(feature = "std")]
85extern crate std;
86
87#[cfg(feature = "alloc")]
88#[allow(unused_extern_crates)]
89extern crate alloc;
90
91mod errors;
92mod meta;
93mod typed;
94
95pub use errors::Error;
96pub use indexbus_codec::CodecId;
97pub use meta::{meta, IndexbusMsgSpec, MsgMeta};
98
99#[deprecated(note = "Use indexbus_codec::CodecId (re-exported as indexbus_typed::CodecId)")]
100pub use indexbus_codec::CodecId as HasCodecId;
101pub use typed::{
102    TypedFanoutConsumer, TypedFanoutMpscProducer, TypedFanoutProducer, TypedMpscConsumer,
103    TypedMpscProducer, TypedSpscReceiver, TypedSpscSender,
104};
105
106/// Curated prelude.
107pub mod prelude {
108    pub use crate::typed::{
109        TypedFanoutConsumer, TypedFanoutMpscProducer, TypedFanoutProducer, TypedMpscConsumer,
110        TypedMpscProducer, TypedSpscReceiver, TypedSpscSender,
111    };
112    pub use crate::{meta, CodecId, Error, IndexbusMsgSpec, MsgMeta};
113
114    #[allow(deprecated)]
115    pub use crate::HasCodecId;
116}
117
118#[cfg(test)]
119mod tests;