ic_dbms_macros/lib.rs
1#![crate_name = "ic_dbms_macros"]
2#![crate_type = "lib"]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5//! Macros and derive for ic-dbms-canister
6//!
7//! This crate provides procedural macros to automatically implement traits
8//! required by the `ic-dbms-canister`.
9//!
10//! ## Provided Derive Macros
11//!
12//! - `Encode`: Automatically implements the `Encode` trait for structs.
13//!
14
15#![doc(html_playground_url = "https://play.rust-lang.org")]
16#![doc(
17 html_favicon_url = "https://raw.githubusercontent.com/veeso/ic-dbms-canister/main/assets/images/cargo/logo-128.png"
18)]
19#![doc(
20 html_logo_url = "https://raw.githubusercontent.com/veeso/ic-dbms-canister/main/assets/images/cargo/logo-512.png"
21)]
22
23use proc_macro::TokenStream;
24use syn::{DeriveInput, parse_macro_input};
25
26mod encode;
27mod utils;
28
29/// Automatically implements the `Encode`` trait for a struct.
30///
31/// This derive macro generates two methods required by the `Encode` trait:
32///
33/// - `fn data_size() -> DataSize`
34/// Computes the static size of the encoded type.
35/// If all fields implement `Encode::data_size()` returning
36/// `DataSize::Fixed(n)`, then the type is also considered fixed-size.
37/// Otherwise, the type is `DataSize::Dynamic`.
38///
39/// - `fn size(&self) -> MSize`
40/// Computes the runtime-encoding size of the value by summing the
41/// sizes of all fields.
42///
43/// # What the macro generates
44///
45/// Given a struct like:
46///
47/// ```rust,ignore
48/// #[derive(Encode)]
49/// struct User {
50/// id: Uint32,
51/// name: Text,
52/// }
53/// ```
54///
55/// The macro expands into:
56///
57/// ```rust,ignore
58/// impl Encode for User {
59/// const DATA_SIZE: DataSize = DataSize::Dynamic; // or DataSize::Fixed(n) if applicable
60///
61/// fn size(&self) -> MSize {
62/// self.id.size() + self.name.size()
63/// }
64///
65/// fn encode(&'_ self) -> std::borrow::Cow<'_, [u8]> {
66/// let mut encoded = Vec::with_capacity(self.size() as usize);
67/// encoded.extend_from_slice(&self.id.encode());
68/// encoded.extend_from_slice(&self.name.encode());
69/// std::borrow::Cow::Owned(encoded)
70/// }
71///
72/// fn decode(data: std::borrow::Cow<[u8]>) -> ::ic_dbms_canister::prelude::MemoryResult<Self> {
73/// let mut offset = 0;
74/// let id = Uint32::decode(std::borrow::Borrowed(&data[offset..]))?;
75/// offset += id.size() as usize;
76/// let name = Text::decode(std::borrow::Borrowed(&data[offset..]))?;
77/// offset += name.size() as usize;
78/// Ok(Self { id, name })
79/// }
80/// }
81/// ```
82/// # Requirements
83///
84/// - Each field type must implement `Encode`.
85/// - Only works on `struct`s; enums and unions are not supported.
86/// - All field identifiers must be valid Rust identifiers (no tuple structs).
87///
88/// # Notes
89///
90/// - It is intended for internal use within the `ic-dbms-canister` DBMS memory
91/// system.
92///
93/// # Errors
94///
95/// The macro will fail to expand if:
96///
97/// - The struct has unnamed fields (tuple struct)
98/// - A field type does not implement `Encode`
99/// - The macro is applied to a non-struct item.
100///
101/// # Example
102///
103/// ```rust,ignore
104/// #[derive(Encode, Debug, PartialEq, Eq)]
105/// struct Position {
106/// x: Int32,
107/// y: Int32,
108/// }
109///
110/// let pos = Position { x: 10.into(), y: 20.into() };
111/// assert_eq!(Position::data_size(), DataSize::Fixed(8));
112/// assert_eq!(pos.size(), 8);
113/// let encoded = pos.encode();
114/// let decoded = Position::decode(encoded).unwrap();
115/// assert_eq!(pos, decoded);
116/// ```
117#[proc_macro_derive(Encode)]
118pub fn derive_encode(input: TokenStream) -> TokenStream {
119 let input = parse_macro_input!(input as DeriveInput);
120 self::encode::encode(input)
121}