ledb_derive/
lib.rs

1/*!
2
3# Derive macro for defining storable documents
4
5This crate helps to turn rust structures into documents which can be stored, indexed and queried.
6
7## Defining documents
8
9You may turn any struct into a document using `Document` in derive annotation like this:
10
11```rust
12use serde::{Serialize, Deserialize};
13use ledb::{Document};
14
15#[derive(Serialize, Deserialize, Document)]
16struct MyDoc {
17    // primary field
18    #[document(primary)]
19    id: Option<u32>,
20    // other fields
21}
22```
23
24This generates `Document` trait implementation for struct `MyDoc`.
25It requires single field marked as primary key per document.
26Currently primary key should be an integer only.
27Also it not needed to be an optional field, but in this case you should take care of parsing (for example add `serde(default)` annotation).
28
29## Defining key fields for indexing
30
31To turn document field into key you can add document index annotation to it:
32
33```rust
34# extern crate serde;
35# extern crate ledb;
36#
37use serde::{Serialize, Deserialize};
38use ledb::{Document};
39
40#[derive(Serialize, Deserialize, Document)]
41struct MyDoc {
42    // primary field
43    #[serde(default)]
44    #[document(primary)]
45    id: u32,
46    // unique string key
47    #[document(unique)]
48    title: String,
49    // normal string index
50    #[document(index)]
51    keywords: Vec<String>,
52    // unique int key
53    #[document(unique)]
54    timestamp: u64,
55}
56```
57
58## Overriding key types
59
60In some cases it may be ambiguous to determine actual type of key by field type.
61For example, when you try to index binary data using `Vec<u8>`, the actually determined key type is an integer (u8).
62So you required to override key type manually using annotation like so:
63
64```rust
65# extern crate serde;
66# extern crate serde_bytes;
67# extern crate ledb;
68#
69use serde::{Serialize, Deserialize};
70use serde_bytes;
71use ledb::{Document};
72
73#[derive(Serialize, Deserialize, Document)]
74struct MyDoc {
75    #[document(primary)]
76    id: u32,
77    // ...
78    #[document(unique binary)]
79    #[serde(with = "serde_bytes")]
80    hash: Vec<u8>,
81}
82```
83
84## Nested documents
85
86Of course you can add nested documents which may also have key fields:
87
88```rust
89# extern crate serde;
90# extern crate ledb;
91#
92use std::collections::HashMap;
93use serde::{Serialize, Deserialize};
94use ledb::{Document};
95
96#[derive(Serialize, Deserialize, Document)]
97struct MyDoc {
98    // primary field
99    #[document(primary)]
100    #[serde(default)]
101    id: u32,
102    // ...fields
103    // simple nested document
104    #[document(nested)]
105    meta: Meta,
106    // list of nested documents
107    #[document(nested)]
108    links: Vec<Link>,
109    // map of nested documents
110    #[document(nested)]
111    props: HashMap<String, Prop>,
112}
113
114#[derive(Serialize, Deserialize, Document)]
115#[document(nested)]
116struct Meta {
117    #[document(index)]
118    title: String,
119    #[document(index)]
120    author: String,
121    annotation: String,
122}
123
124#[derive(Serialize, Deserialize, Document)]
125#[document(nested)]
126struct Link {
127    href: String,
128    text: String,
129}
130
131#[derive(Serialize, Deserialize, Document)]
132#[document(nested)]
133struct Prop {
134    value: String,
135    required: bool,
136}
137```
138
139The primary key field is omitted for nested documents.
140The nested documents should be explicitly marked as nested using `#[document(nested)]` directive as shown above.
141
142**NOTE**: When the `#[serde(flatten)]` directive is used the key fields of nested documents will be transferred to owner.
143
144## Simple usage example
145
146```rust
147# extern crate serde;
148# extern crate ledb;
149#
150use serde::{Serialize, Deserialize};
151use ledb::{Document};
152
153#[derive(Serialize, Deserialize, Document)]
154struct MyDoc {
155    // define optional primary key field
156    #[document(primary)]
157    id: Option<u64>,
158    // define unique key field
159    #[document(unique)]
160    title: String,
161    // define index fields
162    #[document(index)]
163    tag: Vec<String>,
164    #[document(unique)]
165    timestamp: u32,
166    // define nested document
167    #[document(nested)]
168    meta: MetaData,
169}
170
171#[derive(Serialize, Deserialize, Document)]
172#[document(nested)]
173struct MetaData {
174    // define index field
175    #[document(index)]
176    keywords: Vec<String>,
177    // define other fields
178    description: String,
179}
180```
181
182It will generate the `Document` traits like so:
183
184```ignore
185impl Document for MyDoc {
186    // declare primary key field name
187    fn primary_field() -> Identifier {
188        "id".into()
189    }
190
191    // declare other key fields for index
192    fn key_fields() -> KeyFields {
193        KeyFields::new()
194            // add key fields of document
195            .with_field(("title", String::key_type(), IndexKind::Unique))
196            .with_field(("tag", String::key_type(), IndexKind::Index))
197            .with_field(("timestamp", u32::key_type(), IndexKind::Unique))
198            // add key fields from nested document
199            .with_fields(MetaData::key_fields().with_parent("meta"))
200    }
201}
202
203impl Document for MetaData {
204    // declare key fields for index
205    fn key_fields() -> KeyFields {
206        KeyFields::new()
207            // add key fields of document
208            .with_field(("keywords", KeyType::String, IndexKind::Index))
209    }
210}
211```
212
213*/
214
215mod document;
216mod wrapper;
217
218use document::derive_document_wrapped;
219use proc_macro::TokenStream;
220use quote::quote;
221use syn::{parse_macro_input, DeriveInput};
222
223#[proc_macro_derive(Document, attributes(document))]
224pub fn derive_document(input: TokenStream) -> TokenStream {
225    let input = parse_macro_input!(input as DeriveInput);
226    derive_document_wrapped(&input)
227        .unwrap_or_else(compile_error)
228        .into()
229}
230
231fn compile_error(message: String) -> proc_macro2::TokenStream {
232    quote! {
233        compile_error!(#message);
234    }
235}