nix_compat_derive/
lib.rs

1//! # Using derive
2//!
3//! 1. [Overview](#overview)
4//! 3. [Attributes](#attributes)
5//!     1. [Container attributes](#container-attributes)
6//!         1. [`#[nix(from_str)]`](#nixfrom_str)
7//!         2. [`#[nix(from = "FromType")]`](#nixfrom--fromtype)
8//!         3. [`#[nix(try_from = "FromType")]`](#nixtry_from--fromtype)
9//!         4. [`#[nix(into = "IntoType")]`](#nixinto--intotype)
10//!         5. [`#[nix(try_into = "IntoType")]`](#nixtry_into--intotype)
11//!         6. [`#[nix(display)]`](#nixdisplay)
12//!         7. [`#[nix(display = "path")]`](#nixdisplay--path)
13//!         8. [`#[nix(crate = "...")]`](#nixcrate--)
14//!     2. [Variant attributes](#variant-attributes)
15//!         1. [`#[nix(version = "range")]`](#nixversion--range)
16//!     3. [Field attributes](#field-attributes)
17//!         1. [`#[nix(version = "range")]`](#nixversion--range-1)
18//!         2. [`#[nix(default)]`](#nixdefault)
19//!         3. [`#[nix(default = "path")]`](#nixdefault--path)
20//!
21//! ## Overview
22//!
23//! This crate contains derive macros and function-like macros for implementing
24//! `NixDeserialize` and `NixSerialize` with less boilerplate.
25//!
26//! ### Examples
27//!
28//! ```rust
29//! # use nix_compat_derive::{NixDeserialize, NixSerialize};
30//! #
31//! #[derive(NixDeserialize, NixSerialize)]
32//! struct Unnamed(u64, String);
33//! ```
34//!
35//! ```rust
36//! # use nix_compat_derive::{NixDeserialize, NixSerialize};
37//! #
38//! #[derive(NixDeserialize, NixSerialize)]
39//! struct Fields {
40//!     number: u64,
41//!     message: String,
42//! };
43//! ```
44//!
45//! ```rust
46//! # use nix_compat_derive::{NixDeserialize, NixSerialize};
47//! #
48//! #[derive(NixDeserialize, NixSerialize)]
49//! struct Ignored;
50//! ```
51//!
52//! ## Attributes
53//!
54//! To customize the derived trait implementations you can add
55//! [attributes](https://doc.rust-lang.org/reference/attributes.html)
56//! to containers, fields and variants.
57//!
58//! ```rust
59//! # use nix_compat_derive::NixDeserialize;
60//! #
61//! #[derive(NixDeserialize)]
62//! #[nix(crate="nix_compat")] // <-- This is a container attribute
63//! struct Fields {
64//!     number: u64,
65//!     #[nix(version="..20")] // <-- This is a field attribute
66//!     message: String,
67//! };
68//!
69//! #[derive(NixDeserialize)]
70//! #[nix(crate="nix_compat")] // <-- This is also a container attribute
71//! enum E {
72//!     #[nix(version="..10")] // <-- This is a variant attribute
73//!     A(u64),
74//!     #[nix(version="10..")] // <-- This is also a variant attribute
75//!     B(String),
76//! }
77//! ```
78//!
79//! ### Container attributes
80//!
81//! ##### `#[nix(from_str)]`
82//!
83//! When `from_str` is specified the fields are all ignored and instead a
84//! `String` is first deserialized and then `FromStr::from_str` is used
85//! to convert this `String` to the container type.
86//!
87//! This means that the container must implement `FromStr` and the error
88//! returned from the `from_str` must implement `Display`.
89//!
90//! ###### Example
91//!
92//! ```rust
93//! # use nix_compat_derive::NixDeserialize;
94//! #
95//! #[derive(NixDeserialize)]
96//! #[nix(from_str)]
97//! struct MyString(String);
98//! impl std::str::FromStr for MyString {
99//!     type Err = String;
100//!     fn from_str(s: &str) -> Result<Self, Self::Err> {
101//!         if s != "bad string" {
102//!             Ok(MyString(s.to_string()))
103//!         } else {
104//!             Err("Got a bad string".to_string())
105//!         }
106//!     }
107//! }
108//! ```
109//!
110//! ##### `#[nix(from = "FromType")]`
111//!
112//! When `from` is specified the fields are all ignored and instead a
113//! value of `FromType` is first deserialized and then `From::from` is
114//! used to convert from this value to the container type.
115//!
116//! This means that the container must implement `From<FromType>` and
117//! `FromType` must implement `NixDeserialize`.
118//!
119//! ###### Example
120//!
121//! ```rust
122//! # use nix_compat_derive::NixDeserialize;
123//! #
124//! #[derive(NixDeserialize)]
125//! #[nix(from="usize")]
126//! struct MyValue(usize);
127//! impl From<usize> for MyValue {
128//!     fn from(val: usize) -> Self {
129//!         MyValue(val)
130//!     }
131//! }
132//! ```
133//!
134//! ##### `#[nix(try_from = "FromType")]`
135//!
136//! With `try_from` a value of `FromType` is first deserialized and then
137//! `TryFrom::try_from` is used to convert from this value to the container
138//! type.
139//!
140//! This means that the container must implement `TryFrom<FromType>` and
141//! `FromType` must implement `NixDeserialize`.
142//! The error returned from `try_from` also needs to implement `Display`.
143//!
144//! ###### Example
145//!
146//! ```rust
147//! # use nix_compat_derive::NixDeserialize;
148//! #
149//! #[derive(NixDeserialize)]
150//! #[nix(try_from="usize")]
151//! struct WrongAnswer(usize);
152//! impl TryFrom<usize> for WrongAnswer {
153//!     type Error = String;
154//!     fn try_from(val: usize) -> Result<Self, Self::Error> {
155//!         if val != 42 {
156//!             Ok(WrongAnswer(val))
157//!         } else {
158//!             Err("Got the answer to life the universe and everything".to_string())
159//!         }
160//!     }
161//! }
162//! ```
163//!
164//! ##### `#[nix(into = "IntoType")]`
165//!
166//! When `into` is specified the fields are all ignored and instead the
167//! container type is converted to `IntoType` using `Into::into` and
168//! `IntoType` is then serialized. Before converting `Clone::clone` is
169//! called.
170//!
171//! This means that the container must implement `Into<IntoType>` and `Clone`
172//! and `IntoType` must implement `NixSerialize`.
173//!
174//! ###### Example
175//!
176//! ```rust
177//! # use nix_compat_derive::NixSerialize;
178//! #
179//! #[derive(Clone, NixSerialize)]
180//! #[nix(into="usize")]
181//! struct MyValue(usize);
182//! impl From<MyValue> for usize {
183//!     fn from(val: MyValue) -> Self {
184//!         val.0
185//!     }
186//! }
187//! ```
188//!
189//! ##### `#[nix(try_into = "IntoType")]`
190//!
191//! When `try_into` is specified the fields are all ignored and instead the
192//! container type is converted to `IntoType` using `TryInto::try_into` and
193//! `IntoType` is then serialized. Before converting `Clone::clone` is
194//! called.
195//!
196//! This means that the container must implement `TryInto<IntoType>` and
197//! `Clone` and `IntoType` must implement `NixSerialize`.
198//! The error returned from `try_into` also needs to implement `Display`.
199//!
200//! ###### Example
201//!
202//! ```rust
203//! # use nix_compat_derive::NixSerialize;
204//! #
205//! #[derive(Clone, NixSerialize)]
206//! #[nix(try_into="usize")]
207//! struct WrongAnswer(usize);
208//! impl TryFrom<WrongAnswer> for usize {
209//!     type Error = String;
210//!     fn try_from(val: WrongAnswer) -> Result<Self, Self::Error> {
211//!         if val.0 != 42 {
212//!             Ok(val.0)
213//!         } else {
214//!             Err("Got the answer to life the universe and everything".to_string())
215//!         }
216//!     }
217//! }
218//! ```
219//!
220//! ##### `#[nix(display)]`
221//!
222//! When `display` is specified the fields are all ignored and instead the
223//! container must implement `Display` and `NixWrite::write_display` is used to
224//! write the container.
225//!
226//! ###### Example
227//!
228//! ```rust
229//! # use nix_compat_derive::NixSerialize;
230//! # use std::fmt::{Display, Result, Formatter};
231//! #
232//! #[derive(NixSerialize)]
233//! #[nix(display)]
234//! struct WrongAnswer(usize);
235//! impl Display for WrongAnswer {
236//!     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
237//!         write!(f, "Wrong Answer = {}", self.0)
238//!     }
239//! }
240//! ```
241//!
242//! ##### `#[nix(display = "path")]`
243//!
244//! When `display` is specified the fields are all ignored and instead the
245//! container the specified path must point to a function that is callable as
246//! `fn(&T) -> impl Display`. The result from this call is then written with
247//! `NixWrite::write_display`.
248//! For example `default = "my_value"` would call `my_value(&self)` and `display =
249//! "AType::empty"` would call `AType::empty(&self)`.
250//!
251//! ###### Example
252//!
253//! ```rust
254//! # use nix_compat_derive::NixSerialize;
255//! # use std::fmt::{Display, Result, Formatter};
256//! #
257//! #[derive(NixSerialize)]
258//! #[nix(display = "format_it")]
259//! struct WrongAnswer(usize);
260//! struct WrongDisplay<'a>(&'a WrongAnswer);
261//! impl<'a> Display for WrongDisplay<'a> {
262//!     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
263//!         write!(f, "Wrong Answer = {}", self.0.0)
264//!     }
265//! }
266//!
267//! fn format_it(value: &WrongAnswer) -> impl Display + '_ {
268//!     WrongDisplay(value)
269//! }
270//! ```
271//!
272//! ##### `#[nix(crate = "...")]`
273//!
274//! Specify the path to the `nix-compat` crate instance to use when referring
275//! to the API in the generated code. This is usually not needed.
276//!
277//! ### Variant attributes
278//!
279//! ##### `#[nix(version = "range")]`
280//!
281//! Specifies the protocol version range where this variant is used.
282//! When deriving an enum the `version` attribute is used to select which
283//! variant of the enum to deserialize. The range is for minor version and
284//! the version ranges of all variants combined must cover all versions
285//! without any overlap or the first variant that matches is selected.
286//!
287//! ###### Example
288//!
289//! ```rust
290//! # use nix_compat_derive::NixDeserialize;
291//! #
292//! #[derive(NixDeserialize)]
293//! enum Testing {
294//!     #[nix(version="..=18")]
295//!     OldVersion(u64),
296//!     #[nix(version="19..")]
297//!     NewVersion(String),
298//! }
299//! ```
300//!
301//! ### Field attributes
302//!
303//! ##### `#[nix(version = "range")]`
304//!
305//! Specifies the protocol version range where this field is included.
306//! The range is for minor version. For example `version = "..20"`
307//! includes the field in protocol versions `1.0` to `1.19` and skips
308//! it in version `1.20` and above.
309//!
310//! ###### Example
311//!
312//! ```rust
313//! # use nix_compat_derive::NixDeserialize;
314//! #
315//! #[derive(NixDeserialize)]
316//! struct Field {
317//!     number: u64,
318//!     #[nix(version="..20")]
319//!     messsage: String,
320//! }
321//! ```
322//!
323//! ##### `#[nix(default)]`
324//!
325//! When a field is skipped because the active protocol version falls
326//! outside the range specified in [`#[nix(version = "range")]`](#nixversion--range-1)
327//! this attribute indicates that `Default::default()` should be used
328//! to get a value for the field. This is also the default
329//! when you only specify [`#[nix(version = "range")]`](#nixversion--range-1).
330//!
331//! ###### Example
332//!
333//! ```rust
334//! # use nix_compat_derive::NixDeserialize;
335//! #
336//! #[derive(NixDeserialize)]
337//! struct Field {
338//!     number: u64,
339//!     #[nix(version="..20", default)]
340//!     messsage: String,
341//! }
342//! ```
343//!
344//! ##### `#[nix(default = "path")]`
345//!
346//! When a field is skipped because the active protocol version falls
347//! outside the range specified in [`#[nix(version = "range")]`](#nixversion--range-1)
348//! this attribute indicates that the function in `path` should be called to
349//! get a default value for the field. The given function must be callable
350//! as `fn() -> T`.
351//! For example `default = "my_value"` would call `my_value()` and `default =
352//! "AType::empty"` would call `AType::empty()`.
353//!
354//! ###### Example
355//!
356//! ```rust
357//! # use nix_compat_derive::NixDeserialize;
358//! #
359//! #[derive(NixDeserialize)]
360//! struct Field {
361//!     number: u64,
362//!     #[nix(version="..20", default="missing_string")]
363//!     messsage: String,
364//! }
365//!
366//! fn missing_string() -> String {
367//!     "missing string".to_string()
368//! }
369//! ```
370
371use internal::inputs::RemoteInput;
372use proc_macro::TokenStream;
373use syn::{parse_quote, DeriveInput};
374
375mod de;
376mod internal;
377mod ser;
378
379#[proc_macro_derive(NixDeserialize, attributes(nix))]
380pub fn derive_nix_deserialize(item: TokenStream) -> TokenStream {
381    let mut input = syn::parse_macro_input!(item as DeriveInput);
382    let crate_path: syn::Path = parse_quote!(::nix_compat);
383    de::expand_nix_deserialize(crate_path, &mut input)
384        .unwrap_or_else(syn::Error::into_compile_error)
385        .into()
386}
387
388#[proc_macro_derive(NixSerialize, attributes(nix))]
389pub fn derive_nix_serialize(item: TokenStream) -> TokenStream {
390    let mut input = syn::parse_macro_input!(item as DeriveInput);
391    let crate_path: syn::Path = parse_quote!(::nix_compat);
392    ser::expand_nix_serialize(crate_path, &mut input)
393        .unwrap_or_else(syn::Error::into_compile_error)
394        .into()
395}
396
397/// Macro to implement `NixDeserialize` on a type.
398/// Sometimes you can't use the deriver to implement `NixDeserialize`
399/// (like when dealing with types in Rust standard library) but don't want
400/// to implement it yourself. So this macro can be used for those situations
401/// where you would derive using `#[nix(from_str)]`,
402/// `#[nix(from = "FromType")]` or `#[nix(try_from = "FromType")]` if you
403/// could.
404///
405/// #### Example
406///
407/// ```rust
408/// # use nix_compat_derive::nix_deserialize_remote;
409/// #
410/// struct MyU64(u64);
411///
412/// impl From<u64> for MyU64 {
413///     fn from(value: u64) -> Self {
414///         Self(value)
415///     }
416/// }
417///
418/// nix_deserialize_remote!(#[nix(from="u64")] MyU64);
419/// ```
420#[proc_macro]
421pub fn nix_deserialize_remote(item: TokenStream) -> TokenStream {
422    let input = syn::parse_macro_input!(item as RemoteInput);
423    let crate_path = parse_quote!(::nix_compat);
424    de::expand_nix_deserialize_remote(crate_path, &input)
425        .unwrap_or_else(syn::Error::into_compile_error)
426        .into()
427}
428
429/// Macro to implement `NixSerialize` on a type.
430/// Sometimes you can't use the deriver to implement `NixSerialize`
431/// (like when dealing with types in Rust standard library) but don't want
432/// to implement it yourself. So this macro can be used for those situations
433/// where you would derive using `#[nix(display)]`, `#[nix(display = "path")]`,
434/// `#[nix(store_dir_display)]`, `#[nix(into = "IntoType")]` or
435/// `#[nix(try_into = "IntoType")]` if you could.
436///
437/// #### Example
438///
439/// ```rust
440/// # use nix_compat_derive::nix_serialize_remote;
441/// #
442/// #[derive(Clone)]
443/// struct MyU64(u64);
444///
445/// impl From<MyU64> for u64 {
446///     fn from(value: MyU64) -> Self {
447///         value.0
448///     }
449/// }
450///
451/// nix_serialize_remote!(#[nix(into="u64")] MyU64);
452/// ```
453#[proc_macro]
454pub fn nix_serialize_remote(item: TokenStream) -> TokenStream {
455    let input = syn::parse_macro_input!(item as RemoteInput);
456    let crate_path = parse_quote!(::nix_compat);
457    ser::expand_nix_serialize_remote(crate_path, &input)
458        .unwrap_or_else(syn::Error::into_compile_error)
459        .into()
460}