armour/
lib.rs

1#![warn(clippy::unwrap_used)]
2#![cfg_attr(not(feature = "std"), no_std)]
3#[cfg_attr(not(feature = "std"), macro_use)]
4#[cfg(not(feature = "std"))]
5extern crate alloc;
6
7pub use armour_derive::*;
8pub use async_broadcast::RecvError;
9pub use database::{
10    cid::{self, Cid, KeyScheme, KeyType, key_size},
11    db::Database,
12    error::{DbError, DbResult},
13    raw_tree::RawTree,
14    record::{self, Migration, Record},
15    tree::CollectionTree,
16};
17pub use dyn_types::{Typ, get_type::GetType, value::Value};
18#[cfg(feature = "fjall")]
19pub use fjall::{self, Slice};
20pub use indexes::index::{CompositionIndex, HashIndexExtractor};
21pub use rapira;
22#[cfg(feature = "sled")]
23pub use sled::{self, InlineArray};
24pub use types::{entry::Entry, fuid::Fuid, id64::Id64, record_status::RecordStatus};
25#[cfg(all(feature = "sled", feature = "id32"))]
26pub use types::{ident::ID, low_id::LowId, uid::Uid};
27
28#[cfg(feature = "bytemuck")]
29pub use crate::database::byte_as_slice::BytesAsValSlice;
30
31pub mod database;
32pub mod dyn_types;
33pub mod indexes;
34#[cfg(feature = "fjall")]
35pub mod logdb;
36pub mod types;
37pub mod utils;
38
39pub mod migrations {
40    pub use crate::record::{MigrationRes, MigrationType};
41}
42
43/// key should be Base64Url decoded 56 bytes long (~ 75 chars)
44///
45/// `const_hasher!(module_name, "ENV_KEY_NAME");`
46///
47/// env key concatenated with "APP_ID_KEY_" will be used to get the key
48#[macro_export]
49macro_rules! const_hasher {
50    ($m_name: ident, $key_name: literal) => {
51        pub mod $m_name {
52            use $crate::types::enc::{Cipher, IdHasher};
53
54            #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
55            pub struct Hasher;
56
57            impl IdHasher for Hasher {
58                const HASHER: Cipher = Cipher::new(env!(concat!("APP_ID_KEY_", $key_name)));
59            }
60        }
61    };
62}
63
64/// key should be Base64Url decoded 56 bytes long (~ 75 chars)
65///
66/// `hasher!(module_name, "hash_key");`
67#[macro_export]
68macro_rules! hasher {
69    ($m_name: ident, $key: literal) => {
70        pub mod $m_name {
71            use $crate::types::enc::{Cipher, IdHasher};
72
73            #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
74            pub struct Hasher;
75
76            impl IdHasher for Hasher {
77                const HASHER: Cipher = Cipher::new($key);
78            }
79        }
80    };
81}
82
83/// rapira_record!(User, UserID, sled::InlineArray, "users")
84#[macro_export]
85macro_rules! rapira_record {
86    ($ty: ty, $id_ty: ty, $value_ty: ty, $name: literal) => {
87        impl $crate::Record for $ty {
88            type SelfId = $id_ty;
89            type Value = $value_ty;
90            const NAME: &'static str = $name;
91            fn deser(bytes: &Self::Value) -> Self {
92                rapira::deserialize(bytes.as_ref()).unwrap()
93            }
94            fn ser(&self) -> $value_ty {
95                rapira::serialize(self).into()
96            }
97        }
98    };
99}
100
101/// zerocopy_record!(User, UserID, sled::InlineArray, "users")
102#[macro_export]
103macro_rules! zerocopy_record {
104    ($ty: ty, $id_ty: ty, $value_ty: ty, $name: literal) => {
105        const SIZE: usize = std::mem::size_of::<$ty>();
106        impl $crate::Record for $ty {
107            type SelfId = $id_ty;
108            type Value = $value_ty;
109            const NAME: &'static str = $name;
110            fn deser(bytes: &Self::Value) -> Self {
111                let bytes: &[u8] = bytes.as_ref();
112                let bytes: [u8; SIZE] = bytes.try_into().unwrap();
113                zerocopy::transmute!(bytes)
114            }
115            fn ser(&self) -> $value_ty {
116                let bytes: &[u8; SIZE] = zerocopy::transmute_ref!(self);
117                bytes.into()
118            }
119        }
120    };
121}
122
123/// bytemuck_record!(User, UserID, sled::InlineArray, "users")
124#[macro_export]
125macro_rules! bytemuck_slice_record {
126    ($ty: ty, $id_ty: ty, $value_ty: ty, $name: literal) => {
127        impl $crate::Record for $ty {
128            type SelfId = $id_ty;
129            type Value = $value_ty;
130            const NAME: &'static str = $name;
131            fn deser(bytes: &Self::Value) -> Self {
132                if bytes.is_empty() {
133                    return Default::default();
134                }
135                let vec = bytes.as_ref().to_vec();
136                let vec: Vec<ProfileId> = bytemuck::cast_vec(vec);
137                Self(vec)
138            }
139            fn ser(&self) -> $value_ty {
140                let bytes: &[u8] = bytemuck::cast_slice(self);
141                bytes.to_vec().into()
142            }
143        }
144    };
145}
146
147/// `zerocopy_cid!(Key, [KeyType::U64...], Type::GROUP_BITS, field_name)`
148#[macro_export]
149macro_rules! zerocopy_cid {
150    ($ty: ty, [$($key_type:expr),*], $group_bits:expr, $field:ident) => {
151        impl $crate::Cid for $ty {
152            type B = [u8; size_of::<Self>()];
153            const TY: $crate::KeyScheme = $crate::KeyScheme::Typed(&[$($key_type),*]);
154            const GROUP_BITS: u32 = $group_bits;
155            fn encode(&self) -> Self::B {
156                zerocopy::transmute!(*self)
157            }
158            fn decode(bytes: &Self::B) -> armour::types::Result<Self> {
159                Ok(zerocopy::transmute!(*bytes))
160            }
161            fn group_id(&self) -> u32 {
162                self.$field.group_id()
163            }
164        }
165    };
166}
167
168#[cfg(test)]
169mod tests {
170    use rapira::Rapira;
171
172    use super::*;
173    use crate::types::id64::Id64;
174
175    #[derive(Clone, Rapira)]
176    struct User {
177        name: String,
178    }
179
180    impl GetType for User {
181        const TYPE: Typ = Typ::Custom("unimplemented", &[]);
182    }
183
184    const_hasher!(test_key, "TEST");
185    type UserID = Id64<test_key::Hasher>;
186    rapira_record!(User, UserID, Vec<u8>, "users");
187
188    #[test]
189    fn test_rapira_record() {
190        let _user = User {
191            name: "John Doe".to_string(),
192        };
193    }
194}