1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use TokenStream;
/// A derive macro that generates a `field_type()` associated function for a
/// struct.
///
/// The generated method returns a `FieldType::Map` whose keys are the
/// (possibly serde-renamed) field names and whose values are the inferred or
/// explicitly overridden [`anda_db_schema::FieldType`] for each field. It is
/// the building block used by `AndaDBSchema` for nested user-defined types.
///
/// # Attributes
///
/// - `#[field_type = "TypeName"]` -- override the inferred type. The string
/// accepts a small DSL: primitives (`Bytes`, `Text`, `U64`, ...), as well
/// as `Array<T>`, `Option<T>`, `Map<String, T>`, `Map<Text, T>` and
/// `Map<Bytes, T>` (where `T` is itself any supported type, including
/// nested wrappers).
/// - `#[serde(rename = "name")]` -- use the renamed identifier as the
/// schema field name. Other serde options are ignored.
///
/// # Type inference
///
/// When `#[field_type]` is absent, the type is inferred from the Rust type:
///
/// - `String` / `&str` -> `Text`
/// - integers / floats / `bool` -> their numeric `FieldType`
/// - `Vec<u8>`, `[u8; N]`, `Bytes`, `ByteBuf`, `ByteArray`, `*B64` -> `Bytes`
/// - `Vec<bf16>`, `[bf16; N]` -> `Vector`
/// - `Vec<T>` / `HashSet<T>` / `BTreeSet<T>` -> `Array(T)`
/// - `HashMap<K, V>` / `BTreeMap<K, V>` (string- or bytes-like key) -> `Map`
/// - `Option<T>` -> `Option(T)`
/// - `serde_json::Value`, `Json` -> `Json`
/// - any other path -> the type's `field_type()` function (so the type must
/// itself derive `FieldTyped`)
///
/// Standalone `bf16` values are intentionally rejected -- vectors, not
/// scalars, are the supported abstraction.
///
/// # Example
///
/// ```rust,ignore
/// use anda_db_schema::{FieldType, FieldTyped};
/// use ic_auth_types::Xid;
///
/// #[derive(FieldTyped)]
/// struct User {
/// #[field_type = "Bytes"]
/// id: Xid,
/// name: String,
/// age: u32,
/// }
/// ```
/// A derive macro that generates a `schema()` associated function for a
/// struct.
///
/// The generated method builds a fully-formed [`anda_db_schema::Schema`]
/// using `Schema::builder()`, with one `FieldEntry` per field (excluding the
/// mandatory `_id: u64`, which is provided by the builder itself).
///
/// # Attributes
///
/// - `#[field_type = "TypeName"]` -- override the inferred type. Same DSL as
/// for `FieldTyped`; see that macro's docs for the full grammar.
/// - `#[unique]` -- mark the field as having a unique constraint
/// (`FieldEntry::with_unique`).
/// - `#[serde(rename = "name")]` -- use the renamed identifier as the schema
/// field name.
/// - Doc comments (`/// ...`) are concatenated and used as the field
/// description (`FieldEntry::with_description`).
///
/// # Special fields
///
/// The struct **must** declare `_id: u64`. The field is validated at compile
/// time but skipped during code generation -- AndaDB manages the primary
/// key automatically.
///
/// # Example
///
/// ```rust,ignore
/// use anda_db_schema::{FieldEntry, FieldType, Schema, SchemaError};
/// use anda_db_derive::AndaDBSchema;
///
/// #[derive(AndaDBSchema)]
/// struct User {
/// /// AndaDB-managed primary key
/// _id: u64,
/// /// User's unique identifier
/// #[field_type = "Bytes"]
/// #[unique]
/// id: [u8; 12],
/// /// User's display name
/// name: String,
/// /// User's age in years
/// age: Option<u32>,
/// /// Whether the user account is active
/// active: bool,
/// /// User tags for categorization
/// tags: Vec<String>,
/// }
/// ```
///
/// Expands to:
///
/// ```rust,ignore
/// impl User {
/// pub fn schema() -> Result<Schema, SchemaError> {
/// // ... generated schema construction code
/// }
/// }
/// ```