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