scylla_macros/lib.rs
1//! Procedural macros for ScyllaDB Rust Driver.
2//! Intended for use with the `scylla` and `scylla-cql` crates.
3
4// WARNING FOR CONTRIBUTORS
5// Doc comments in this file use a different syntax than normally.
6// Typically you use [some_item](other_crate::path::to::some_item), but this
7// requires `other_crate` to be a dependency of the crate where the
8// doc comment is located.
9// `scylla` and `scylla-cql` can not be dependencies of `scylla-macros`,
10// so we can't use that here. The workaround is to use a relative link
11// to the html files of the item you want to link - see existing doc comments
12// in this file for examples of that. This way the links work in docs for
13// `scylla` and `scylla-cql` crates (but not in `scylla-macros` crate - nothing
14// we can do about that).
15// There are 2 footguns you need to be careful with because of this solutin:
16// - rustdoc can't verify if the links are correct. When you add / modify
17// a link, you need to open the generated docs, and verify that it works
18// both in `scylla` and `scylla-cql`.
19// - Such relative links only work at a given depth, so the macros need to
20// be re-exported at given depth. Currently, the links assume depth 1, in other
21// words the items must be in the crate root (e.g. `scylla::SerializeRow`).
22
23use darling::{FromMeta, ToTokens};
24use proc_macro::TokenStream;
25
26mod parser;
27
28// Flavor of serialization/deserialization macros ({De,S}erialize{Value,Row}).
29#[derive(Copy, Clone, PartialEq, Eq, Default)]
30enum Flavor {
31 #[default]
32 MatchByName,
33 EnforceOrder,
34}
35
36impl FromMeta for Flavor {
37 fn from_string(value: &str) -> darling::Result<Self> {
38 match value {
39 "match_by_name" => Ok(Self::MatchByName),
40 "enforce_order" => Ok(Self::EnforceOrder),
41 _ => Err(darling::Error::unknown_value(value)),
42 }
43 }
44}
45
46mod serialize;
47
48/// Derive macro for the [`SerializeValue`](./serialize/value/trait.SerializeValue.html) trait
49/// which serializes given Rust structure as a User Defined Type (UDT).
50///
51/// At the moment, only structs with named fields are supported.
52///
53/// Serialization will fail if there are some fields in the Rust struct that don't match
54/// to any of the UDT fields.
55///
56/// If there are fields in UDT that are not present in Rust definition:
57/// - serialization will succeed in "match_by_name" flavor (default). Missing
58/// fields in the middle of UDT will be sent as NULLs, missing fields at the end will not be sent
59/// at all.
60/// - serialization will succeed if suffix of UDT fields is missing. If there are missing fields in the
61/// middle it will fail. Note that if "skip_name_checks" is enabled, and the types happen to match,
62/// it is possible for serialization to succeed with unexpected result.
63///
64/// This behavior is the default to support ALTERing UDTs by adding new fields.
65/// You can forbid excess fields in the UDT using `forbid_excess_udt_fields` attribute.
66///
67/// In case of failure, either [`BuiltinTypeCheckError`](./serialize/value/struct.BuiltinTypeCheckError.html)
68/// or [`BuiltinSerializationError`](./serialize/value/struct.BuiltinSerializationError.html)
69/// will be returned.
70///
71/// # Example
72///
73/// A UDT defined like this:
74///
75/// ```text
76/// CREATE TYPE ks.my_udt (a int, b text, c blob);
77/// ```
78///
79/// ...can be serialized using the following struct:
80///
81/// ```rust
82/// # use scylla::SerializeValue;
83/// #[derive(SerializeValue)]
84/// struct MyUdt {
85/// a: i32,
86/// b: Option<String>,
87/// // No "c" field - it is not mandatory by default for all fields to be present
88/// }
89/// ```
90///
91/// # Struct attributes
92///
93/// `#[scylla(flavor = "flavor_name")]`
94///
95/// Allows to choose one of the possible "flavors", i.e. the way how the
96/// generated code will approach serialization. Possible flavors are:
97///
98/// - `"match_by_name"` (default) - the generated implementation _does not
99/// require_ the fields in the Rust struct to be in the same order as the
100/// fields in the UDT. During serialization, the implementation will take
101/// care to serialize the fields in the order which the database expects.
102/// - `"enforce_order"` - the generated implementation _requires_ the fields
103/// in the Rust struct to be in the same order as the fields in the UDT.
104/// If the order is incorrect, type checking/serialization will fail.
105/// This is a less robust flavor than `"match_by_name"`, but should be
106/// slightly more performant as it doesn't need to perform lookups by name.
107///
108/// `#[scylla(crate = crate_name)]`
109///
110/// By default, the code generated by the derive macro will refer to the items
111/// defined by the driver (types, traits, etc.) via the `::scylla` path.
112/// For example, it will refer to the [`SerializeValue`](./serialize/value/trait.SerializeValue.html) trait
113/// using the following path:
114///
115/// ```rust
116/// use ::scylla::_macro_internal::SerializeValue;
117/// ```
118///
119/// Most users will simply add `scylla` to their dependencies, then use
120/// the derive macro and the path above will work. However, there are some
121/// niche cases where this path will _not_ work:
122///
123/// - The `scylla` crate is imported under a different name,
124/// - The `scylla` crate is _not imported at all_ - the macro actually
125/// is defined in the `scylla-macros` crate and the generated code depends
126/// on items defined in `scylla-cql`.
127///
128/// It's not possible to automatically resolve those issues in the procedural
129/// macro itself, so in those cases the user must provide an alternative path
130/// to either the `scylla` or `scylla-cql` crate.
131///
132/// `#[scylla(skip_name_checks)]`
133///
134/// _Specific only to the `enforce_order` flavor._
135///
136/// Skips checking Rust field names against names of the UDT fields. With this
137/// annotation, the generated implementation will allow mismatch between Rust
138/// struct field names and UDT field names, i.e. it's OK if i-th field has a
139/// different name in Rust and in the UDT. Fields are still being type-checked.
140///
141/// `#[scylla(forbid_excess_udt_fields)]`
142///
143/// Forces Rust struct to have all the fields present in UDT, otherwise
144/// serialization fails.
145///
146/// # Field attributes
147///
148/// `#[scylla(rename = "name_in_the_udt")]`
149///
150/// Serializes the field to the UDT struct field with given name instead of
151/// its Rust name.
152///
153/// `#[scylla(skip)]`
154///
155/// Don't use the field during serialization.
156///
157#[proc_macro_derive(SerializeValue, attributes(scylla))]
158pub fn serialize_value_derive(tokens_input: TokenStream) -> TokenStream {
159 match serialize::value::derive_serialize_value(tokens_input) {
160 Ok(t) => t.into_token_stream().into(),
161 Err(e) => e.into_compile_error().into(),
162 }
163}
164
165/// Derive macro for the [`SerializeRow`](./serialize/row/trait.SerializeRow.html) trait
166/// which serializes given Rust structure into bind markers for a CQL statement.
167///
168/// At the moment, only structs with named fields are supported.
169///
170/// Serialization will fail if there are some bind markers/columns in the statement
171/// that don't match to any of the Rust struct fields, _or vice versa_.
172///
173/// In case of failure, either [`BuiltinTypeCheckError`](./serialize/row/struct.BuiltinTypeCheckError.html)
174/// or [`BuiltinSerializationError`](./serialize/row/struct.BuiltinSerializationError.html)
175/// will be returned.
176///
177/// # Example
178///
179/// A UDT defined like this:
180/// Given a table and a query:
181///
182/// ```text
183/// CREATE TABLE ks.my_t (a int PRIMARY KEY, b text, c blob);
184/// INSERT INTO ks.my_t (a, b, c) VALUES (?, ?, ?);
185/// ```
186///
187/// ...the values for the query can be serialized using the following struct:
188///
189/// ```rust
190/// # use scylla::SerializeRow;
191/// #[derive(SerializeRow)]
192/// struct MyValues {
193/// a: i32,
194/// b: Option<String>,
195/// c: Vec<u8>,
196/// }
197/// ```
198///
199/// # Struct attributes
200///
201/// `#[scylla(flavor = "flavor_name")]`
202///
203/// Allows to choose one of the possible "flavors", i.e. the way how the
204/// generated code will approach serialization. Possible flavors are:
205///
206/// - `"match_by_name"` (default) - the generated implementation _does not
207/// require_ the fields in the Rust struct to be in the same order as the
208/// columns/bind markers. During serialization, the implementation will take
209/// care to serialize the fields in the order which the database expects.
210/// - `"enforce_order"` - the generated implementation _requires_ the fields
211/// in the Rust struct to be in the same order as the columns/bind markers.
212/// If the order is incorrect, type checking/serialization will fail.
213/// This is a less robust flavor than `"match_by_name"`, but should be
214/// slightly more performant as it doesn't need to perform lookups by name.
215///
216/// `#[scylla(crate = crate_name)]`
217///
218/// By default, the code generated by the derive macro will refer to the items
219/// defined by the driver (types, traits, etc.) via the `::scylla` path.
220/// For example, it will refer to the [`SerializeRow`](./serialize/row/trait.SerializeRow.html) trait
221/// using the following path:
222///
223/// ```rust
224/// use ::scylla::_macro_internal::SerializeRow;
225/// ```
226///
227/// Most users will simply add `scylla` to their dependencies, then use
228/// the derive macro and the path above will work. However, there are some
229/// niche cases where this path will _not_ work:
230///
231/// - The `scylla` crate is imported under a different name,
232/// - The `scylla` crate is _not imported at all_ - the macro actually
233/// is defined in the `scylla-macros` crate and the generated code depends
234/// on items defined in `scylla-cql`.
235///
236/// It's not possible to automatically resolve those issues in the procedural
237/// macro itself, so in those cases the user must provide an alternative path
238/// to either the `scylla` or `scylla-cql` crate.
239///
240/// `#[scylla(skip_name_checks)]
241///
242/// _Specific only to the `enforce_order` flavor._
243///
244/// Skips checking Rust field names against names of the columns / bind markers.
245/// With this annotation, the generated implementation will allow mismatch
246/// between Rust struct field names and the column / bind markers, i.e. it's
247/// OK if i-th Rust struct field has a different name than the column / bind
248/// marker. The values are still being type-checked.
249///
250/// # Field attributes
251///
252/// `#[scylla(rename = "column_or_bind_marker_name")]`
253///
254/// Serializes the field to the column / bind marker with given name instead of
255/// its Rust name.
256///
257/// `#[scylla(skip)]`
258///
259/// Don't use the field during serialization.
260///
261/// `#[scylla(flatten)]`
262///
263/// Inline fields from a field into the parent struct. In other words, use this field's
264/// `SerializeRow` implementation to serialize into possibly multiple columns as part of the parent
265/// struct's serialization process.
266///
267/// Note that the name of this field is ignored and hence the `rename` attribute does not make sense
268/// here and will cause a compilation error.
269#[proc_macro_derive(SerializeRow, attributes(scylla))]
270pub fn serialize_row_derive(tokens_input: TokenStream) -> TokenStream {
271 match serialize::row::derive_serialize_row(tokens_input) {
272 Ok(t) => t.into_token_stream().into(),
273 Err(e) => e.into_compile_error().into(),
274 }
275}
276
277mod deserialize;
278
279/// Derive macro for the [`DeserializeRow`](./deserialize/row/trait.DeserializeRow.html)
280/// trait that generates an implementation which deserializes a row with
281/// a similar layout to the Rust struct.
282///
283/// At the moment, only structs with named fields are supported.
284///
285/// This macro properly supports structs with lifetimes, meaning that you can
286/// deserialize columns that borrow memory from the serialized response.
287///
288/// # Example
289///
290/// Having a table defined like this:
291///
292/// ```text
293/// CREATE TABLE ks.my_table (a PRIMARY KEY, b text, c blob);
294/// ```
295///
296/// results of a query "SELECT * FROM ks.my_table"
297/// or "SELECT a, b, c FROM ks.my_table"
298/// can be deserialized using the following struct:
299///
300/// ```rust
301/// # use scylla::DeserializeRow;
302/// #[derive(DeserializeRow)]
303/// # #[scylla(crate = "scylla_cql")]
304/// struct MyRow<'a> {
305/// a: i32,
306/// b: Option<String>,
307/// c: &'a [u8],
308/// }
309/// ```
310///
311/// In general, the struct must match the queried names and types,
312/// not the table itself. For example, the query
313/// "SELECT a AS b FROM ks.my_table" executed against
314/// the aforementioned table can be deserialized to the struct:
315/// ```rust
316/// # use scylla::DeserializeRow;
317/// #[derive(DeserializeRow)]
318/// # #[scylla(crate = "scylla_cql")]
319/// struct MyRow {
320/// b: i32,
321/// }
322/// ```
323///
324/// # Attributes
325///
326/// The macro supports a number of attributes that customize the generated
327/// implementation. Many of the attributes were inspired by procedural macros
328/// from `serde` and try to follow the same naming conventions.
329///
330/// ## Struct attributes
331///
332/// `#[scylla(crate = "crate_name")]`
333///
334/// By default, the code generated by the derive macro will refer to the items
335/// defined by the driver (types, traits, etc.) via the `::scylla` path.
336/// For example, it will refer to the [`DeserializeValue`](./deserialize/value/trait.DeserializeValue.html)
337/// trait using the following path:
338///
339/// ```rust
340/// use ::scylla::_macro_internal::DeserializeValue;
341/// ```
342///
343/// Most users will simply add `scylla` to their dependencies, then use
344/// the derive macro and the path above will work. However, there are some
345/// niche cases where this path will _not_ work:
346///
347/// - The `scylla` crate is imported under a different name,
348/// - The `scylla` crate is _not imported at all_ - the macro actually
349/// is defined in the `scylla-macros` crate and the generated code depends
350/// on items defined in `scylla-cql`.
351///
352/// It's not possible to automatically resolve those issues in the procedural
353/// macro itself, so in those cases the user must provide an alternative path
354/// to either the `scylla` or `scylla-cql` crate.
355///
356/// `#[scylla(flavor = "flavor_name")]`
357///
358/// Allows to choose one of the possible "flavors", i.e. the way how the
359/// generated code will approach deserialization. Possible flavors are:
360///
361/// - `"match_by_name"` (default) - the generated implementation _does not
362/// require_ the fields in the Rust struct to be in the same order as the
363/// columns in the row. During deserialization, the implementation will take
364/// care to deserialize the columns in the order which the database provided.
365/// - `"enforce_order"` - the generated implementation _requires_ the fields
366/// in the Rust struct to be in the same order as the columns in the row.
367/// If the order is incorrect, type checking/deserialization will fail.
368/// This is a less robust flavor than `"match_by_name"`, but should be
369/// slightly more performant as it doesn't need to perform lookups by name.
370/// The generated code will still check that the column and field names match.
371///
372/// #[(scylla(skip_name_checks))]
373///
374/// This attribute only works when used with `flavor = "enforce_order"`.
375///
376/// If set, the generated implementation will not verify the column names at
377/// all. Because it only works with `enforce_order`, it will deserialize first
378/// column into the first field, second column into the second field and so on.
379/// It will still still verify that the column types and field types match.
380///
381/// ## Field attributes
382///
383/// `#[scylla(skip)]`
384///
385/// The field will be completely ignored during deserialization and will
386/// be initialized with `Default::default()`.
387///
388/// `#[scylla(rename = "field_name")]`
389///
390/// By default, the generated implementation will try to match the Rust field
391/// to a column with the same name. This attribute allows to match to a column
392/// with provided name.
393#[proc_macro_derive(DeserializeRow, attributes(scylla))]
394pub fn deserialize_row_derive(tokens_input: TokenStream) -> TokenStream {
395 match deserialize::row::deserialize_row_derive(tokens_input) {
396 Ok(tokens) => tokens.into_token_stream().into(),
397 Err(err) => err.into_compile_error().into(),
398 }
399}
400
401/// Derive macro for the [`DeserializeValue`](./deserialize/value/trait.DeserializeValue.html)
402/// trait that generates an implementation which deserializes a User Defined Type
403/// with the same layout as the Rust struct.
404///
405/// At the moment, only structs with named fields are supported.
406///
407/// This macro properly supports structs with lifetimes, meaning that you can
408/// deserialize UDTs with fields that borrow memory from the serialized response.
409///
410/// # Example
411///
412/// A UDT defined like this:
413///
414/// ```text
415/// CREATE TYPE ks.my_udt (a i32, b text, c blob);
416/// ```
417///
418/// ...can be deserialized using the following struct:
419///
420/// ```rust
421/// # use scylla::DeserializeValue;
422/// #[derive(DeserializeValue)]
423/// # #[scylla(crate = "scylla_cql")]
424/// struct MyUdt<'a> {
425/// a: i32,
426/// b: Option<String>,
427/// c: &'a [u8],
428/// }
429/// ```
430///
431/// # Attributes
432///
433/// The macro supports a number of attributes that customize the generated
434/// implementation. Many of the attributes were inspired by procedural macros
435/// from `serde` and try to follow the same naming conventions.
436///
437/// ## Struct attributes
438///
439/// `#[scylla(crate = "crate_name")]`
440///
441/// By default, the code generated by the derive macro will refer to the items
442/// defined by the driver (types, traits, etc.) via the `::scylla` path.
443/// For example, it will refer to the [`DeserializeValue`](./deserialize/value/trait.DeserializeValue.html)
444/// trait using the following path:
445///
446/// ```rust
447/// use ::scylla::_macro_internal::DeserializeValue;
448/// ```
449///
450/// Most users will simply add `scylla` to their dependencies, then use
451/// the derive macro and the path above will work. However, there are some
452/// niche cases where this path will _not_ work:
453///
454/// - The `scylla` crate is imported under a different name,
455/// - The `scylla` crate is _not imported at all_ - the macro actually
456/// is defined in the `scylla-macros` crate and the generated code depends
457/// on items defined in `scylla-cql`.
458///
459/// It's not possible to automatically resolve those issues in the procedural
460/// macro itself, so in those cases the user must provide an alternative path
461/// to either the `scylla` or `scylla-cql` crate.
462///
463///
464/// `#[scylla(flavor = "flavor_name")]`
465///
466/// Allows to choose one of the possible "flavors", i.e. the way how the
467/// generated code will approach deserialization. Possible flavors are:
468///
469/// - `"match_by_name"` (default) - the generated implementation _does not
470/// require_ the fields in the Rust struct to be in the same order as the
471/// fields in the UDT. During deserialization, the implementation will take
472/// care to deserialize the fields in the order which the database expects.
473/// - `"enforce_order"` - the generated implementation _requires_ the fields
474/// in the Rust struct to be in the same order as the fields in the UDT.
475/// If the order is incorrect, type checking/deserialization will fail.
476/// This is a less robust flavor than `"match_by_name"`, but should be
477/// slightly more performant as it doesn't need to perform lookups by name.
478/// The UDT field names will still be checked during the type check phase.
479///
480/// #[(scylla(skip_name_checks))]
481///
482/// This attribute only works when used with `flavor = "enforce_order"`.
483///
484/// If set, the generated implementation will not verify the UDT field names at
485/// all. Because it only works with `enforce_order`, it will deserialize first
486/// UDT field into the first struct field, second UDT field into the second
487/// struct field and so on. It will still verify that the UDT field types
488/// and struct field types match.
489///
490/// #[(scylla(forbid_excess_udt_fields))]
491///
492/// By default, the generated deserialization code ignores excess UDT fields.
493/// I.e., `enforce_order` flavour ignores excess UDT fields in the suffix
494/// of the UDT definition, and the default unordered flavour ignores excess
495/// UDT fields anywhere.
496/// If more strictness is desired, this flag makes sure that no excess fields
497/// are present and forces error in case there are some.
498///
499/// ## Field attributes
500///
501/// `#[scylla(skip)]`
502///
503/// The field will be completely ignored during deserialization and will
504/// be initialized with `Default::default()`.
505///
506/// `#[scylla(allow_missing)]`
507///
508/// If the UDT definition does not contain this field, it will be initialized
509/// with `Default::default()`.
510///
511/// `#[scylla(default_when_null)]`
512///
513/// If the value of the field received from DB is null, the field will be
514/// initialized with `Default::default()`.
515///
516/// `#[scylla(rename = "field_name")]`
517///
518/// By default, the generated implementation will try to match the Rust field
519/// to a UDT field with the same name. This attribute instead allows to match
520/// to a UDT field with provided name.
521#[proc_macro_derive(DeserializeValue, attributes(scylla))]
522pub fn deserialize_value_derive(tokens_input: TokenStream) -> TokenStream {
523 match deserialize::value::deserialize_value_derive(tokens_input) {
524 Ok(tokens) => tokens.into_token_stream().into(),
525 Err(err) => err.into_compile_error().into(),
526 }
527}