//! # Using derive //! //! 1. [Overview](#overview) //! 3. [Attributes](#attributes) //! 1. [Container attributes](#container-attributes) //! 1. [`#[nix(from_str)]`](#nixfrom_str) //! 2. [`#[nix(from = "FromType")]`](#nixfrom--fromtype) //! 3. [`#[nix(try_from = "FromType")]`](#nixtry_from--fromtype) //! 4. [`#[nix(into = "IntoType")]`](#nixinto--intotype) //! 5. [`#[nix(try_into = "IntoType")]`](#nixtry_into--intotype) //! 6. [`#[nix(display)]`](#nixdisplay) //! 7. [`#[nix(display = "path")]`](#nixdisplay--path) //! 8. [`#[nix(crate = "...")]`](#nixcrate--) //! 2. [Variant attributes](#variant-attributes) //! 1. [`#[nix(version = "range")]`](#nixversion--range) //! 3. [Field attributes](#field-attributes) //! 1. [`#[nix(version = "range")]`](#nixversion--range-1) //! 2. [`#[nix(default)]`](#nixdefault) //! 3. [`#[nix(default = "path")]`](#nixdefault--path) //! //! ## Overview //! //! This crate contains derive macros and function-like macros for implementing //! `NixDeserialize` and `NixSerialize` with less boilerplate. //! //! ### Examples //! //! ```rust //! # use nix_compat_derive::{NixDeserialize, NixSerialize}; //! # //! #[derive(NixDeserialize, NixSerialize)] //! struct Unnamed(u64, String); //! ``` //! //! ```rust //! # use nix_compat_derive::{NixDeserialize, NixSerialize}; //! # //! #[derive(NixDeserialize, NixSerialize)] //! struct Fields { //! number: u64, //! message: String, //! }; //! ``` //! //! ```rust //! # use nix_compat_derive::{NixDeserialize, NixSerialize}; //! # //! #[derive(NixDeserialize, NixSerialize)] //! struct Ignored; //! ``` //! //! ## Attributes //! //! To customize the derived trait implementations you can add //! [attributes](https://doc.rust-lang.org/reference/attributes.html) //! to containers, fields and variants. //! //! ```rust //! # use nix_compat_derive::NixDeserialize; //! # //! #[derive(NixDeserialize)] //! #[nix(crate="nix_compat")] // <-- This is a container attribute //! struct Fields { //! number: u64, //! #[nix(version="..20")] // <-- This is a field attribute //! message: String, //! }; //! //! #[derive(NixDeserialize)] //! #[nix(crate="nix_compat")] // <-- This is also a container attribute //! enum E { //! #[nix(version="..10")] // <-- This is a variant attribute //! A(u64), //! #[nix(version="10..")] // <-- This is also a variant attribute //! B(String), //! } //! ``` //! //! ### Container attributes //! //! ##### `#[nix(from_str)]` //! //! When `from_str` is specified the fields are all ignored and instead a //! `String` is first deserialized and then `FromStr::from_str` is used //! to convert this `String` to the container type. //! //! This means that the container must implement `FromStr` and the error //! returned from the `from_str` must implement `Display`. //! //! ###### Example //! //! ```rust //! # use nix_compat_derive::NixDeserialize; //! # //! #[derive(NixDeserialize)] //! #[nix(from_str)] //! struct MyString(String); //! impl std::str::FromStr for MyString { //! type Err = String; //! fn from_str(s: &str) -> Result { //! if s != "bad string" { //! Ok(MyString(s.to_string())) //! } else { //! Err("Got a bad string".to_string()) //! } //! } //! } //! ``` //! //! ##### `#[nix(from = "FromType")]` //! //! When `from` is specified the fields are all ignored and instead a //! value of `FromType` is first deserialized and then `From::from` is //! used to convert from this value to the container type. //! //! This means that the container must implement `From` and //! `FromType` must implement `NixDeserialize`. //! //! ###### Example //! //! ```rust //! # use nix_compat_derive::NixDeserialize; //! # //! #[derive(NixDeserialize)] //! #[nix(from="usize")] //! struct MyValue(usize); //! impl From for MyValue { //! fn from(val: usize) -> Self { //! MyValue(val) //! } //! } //! ``` //! //! ##### `#[nix(try_from = "FromType")]` //! //! With `try_from` a value of `FromType` is first deserialized and then //! `TryFrom::try_from` is used to convert from this value to the container //! type. //! //! This means that the container must implement `TryFrom` and //! `FromType` must implement `NixDeserialize`. //! The error returned from `try_from` also needs to implement `Display`. //! //! ###### Example //! //! ```rust //! # use nix_compat_derive::NixDeserialize; //! # //! #[derive(NixDeserialize)] //! #[nix(try_from="usize")] //! struct WrongAnswer(usize); //! impl TryFrom for WrongAnswer { //! type Error = String; //! fn try_from(val: usize) -> Result { //! if val != 42 { //! Ok(WrongAnswer(val)) //! } else { //! Err("Got the answer to life the universe and everything".to_string()) //! } //! } //! } //! ``` //! //! ##### `#[nix(into = "IntoType")]` //! //! When `into` is specified the fields are all ignored and instead the //! container type is converted to `IntoType` using `Into::into` and //! `IntoType` is then serialized. Before converting `Clone::clone` is //! called. //! //! This means that the container must implement `Into` and `Clone` //! and `IntoType` must implement `NixSerialize`. //! //! ###### Example //! //! ```rust //! # use nix_compat_derive::NixSerialize; //! # //! #[derive(Clone, NixSerialize)] //! #[nix(into="usize")] //! struct MyValue(usize); //! impl From for usize { //! fn from(val: MyValue) -> Self { //! val.0 //! } //! } //! ``` //! //! ##### `#[nix(try_into = "IntoType")]` //! //! When `try_into` is specified the fields are all ignored and instead the //! container type is converted to `IntoType` using `TryInto::try_into` and //! `IntoType` is then serialized. Before converting `Clone::clone` is //! called. //! //! This means that the container must implement `TryInto` and //! `Clone` and `IntoType` must implement `NixSerialize`. //! The error returned from `try_into` also needs to implement `Display`. //! //! ###### Example //! //! ```rust //! # use nix_compat_derive::NixSerialize; //! # //! #[derive(Clone, NixSerialize)] //! #[nix(try_into="usize")] //! struct WrongAnswer(usize); //! impl TryFrom for usize { //! type Error = String; //! fn try_from(val: WrongAnswer) -> Result { //! if val.0 != 42 { //! Ok(val.0) //! } else { //! Err("Got the answer to life the universe and everything".to_string()) //! } //! } //! } //! ``` //! //! ##### `#[nix(display)]` //! //! When `display` is specified the fields are all ignored and instead the //! container must implement `Display` and `NixWrite::write_display` is used to //! write the container. //! //! ###### Example //! //! ```rust //! # use nix_compat_derive::NixSerialize; //! # use std::fmt::{Display, Result, Formatter}; //! # //! #[derive(NixSerialize)] //! #[nix(display)] //! struct WrongAnswer(usize); //! impl Display for WrongAnswer { //! fn fmt(&self, f: &mut Formatter<'_>) -> Result { //! write!(f, "Wrong Answer = {}", self.0) //! } //! } //! ``` //! //! ##### `#[nix(display = "path")]` //! //! When `display` is specified the fields are all ignored and instead the //! container the specified path must point to a function that is callable as //! `fn(&T) -> impl Display`. The result from this call is then written with //! `NixWrite::write_display`. //! For example `default = "my_value"` would call `my_value(&self)` and `display = //! "AType::empty"` would call `AType::empty(&self)`. //! //! ###### Example //! //! ```rust //! # use nix_compat_derive::NixSerialize; //! # use std::fmt::{Display, Result, Formatter}; //! # //! #[derive(NixSerialize)] //! #[nix(display = "format_it")] //! struct WrongAnswer(usize); //! struct WrongDisplay<'a>(&'a WrongAnswer); //! impl<'a> Display for WrongDisplay<'a> { //! fn fmt(&self, f: &mut Formatter<'_>) -> Result { //! write!(f, "Wrong Answer = {}", self.0.0) //! } //! } //! //! fn format_it(value: &WrongAnswer) -> impl Display + '_ { //! WrongDisplay(value) //! } //! ``` //! //! ##### `#[nix(crate = "...")]` //! //! Specify the path to the `nix-compat` crate instance to use when referring //! to the API in the generated code. This is usually not needed. //! //! ### Variant attributes //! //! ##### `#[nix(version = "range")]` //! //! Specifies the protocol version range where this variant is used. //! When deriving an enum the `version` attribute is used to select which //! variant of the enum to deserialize. The range is for minor version and //! the version ranges of all variants combined must cover all versions //! without any overlap or the first variant that matches is selected. //! //! ###### Example //! //! ```rust //! # use nix_compat_derive::NixDeserialize; //! # //! #[derive(NixDeserialize)] //! enum Testing { //! #[nix(version="..=18")] //! OldVersion(u64), //! #[nix(version="19..")] //! NewVersion(String), //! } //! ``` //! //! ### Field attributes //! //! ##### `#[nix(version = "range")]` //! //! Specifies the protocol version range where this field is included. //! The range is for minor version. For example `version = "..20"` //! includes the field in protocol versions `1.0` to `1.19` and skips //! it in version `1.20` and above. //! //! ###### Example //! //! ```rust //! # use nix_compat_derive::NixDeserialize; //! # //! #[derive(NixDeserialize)] //! struct Field { //! number: u64, //! #[nix(version="..20")] //! messsage: String, //! } //! ``` //! //! ##### `#[nix(default)]` //! //! When a field is skipped because the active protocol version falls //! outside the range specified in [`#[nix(version = "range")]`](#nixversion--range-1) //! this attribute indicates that `Default::default()` should be used //! to get a value for the field. This is also the default //! when you only specify [`#[nix(version = "range")]`](#nixversion--range-1). //! //! ###### Example //! //! ```rust //! # use nix_compat_derive::NixDeserialize; //! # //! #[derive(NixDeserialize)] //! struct Field { //! number: u64, //! #[nix(version="..20", default)] //! messsage: String, //! } //! ``` //! //! ##### `#[nix(default = "path")]` //! //! When a field is skipped because the active protocol version falls //! outside the range specified in [`#[nix(version = "range")]`](#nixversion--range-1) //! this attribute indicates that the function in `path` should be called to //! get a default value for the field. The given function must be callable //! as `fn() -> T`. //! For example `default = "my_value"` would call `my_value()` and `default = //! "AType::empty"` would call `AType::empty()`. //! //! ###### Example //! //! ```rust //! # use nix_compat_derive::NixDeserialize; //! # //! #[derive(NixDeserialize)] //! struct Field { //! number: u64, //! #[nix(version="..20", default="missing_string")] //! messsage: String, //! } //! //! fn missing_string() -> String { //! "missing string".to_string() //! } //! ``` use internal::inputs::RemoteInput; use proc_macro::TokenStream; use syn::{parse_quote, DeriveInput}; mod de; mod internal; mod ser; #[proc_macro_derive(NixDeserialize, attributes(nix))] pub fn derive_nix_deserialize(item: TokenStream) -> TokenStream { let mut input = syn::parse_macro_input!(item as DeriveInput); let crate_path: syn::Path = parse_quote!(::nix_compat); de::expand_nix_deserialize(crate_path, &mut input) .unwrap_or_else(syn::Error::into_compile_error) .into() } #[proc_macro_derive(NixSerialize, attributes(nix))] pub fn derive_nix_serialize(item: TokenStream) -> TokenStream { let mut input = syn::parse_macro_input!(item as DeriveInput); let crate_path: syn::Path = parse_quote!(::nix_compat); ser::expand_nix_serialize(crate_path, &mut input) .unwrap_or_else(syn::Error::into_compile_error) .into() } /// Macro to implement `NixDeserialize` on a type. /// Sometimes you can't use the deriver to implement `NixDeserialize` /// (like when dealing with types in Rust standard library) but don't want /// to implement it yourself. So this macro can be used for those situations /// where you would derive using `#[nix(from_str)]`, /// `#[nix(from = "FromType")]` or `#[nix(try_from = "FromType")]` if you /// could. /// /// #### Example /// /// ```rust /// # use nix_compat_derive::nix_deserialize_remote; /// # /// struct MyU64(u64); /// /// impl From for MyU64 { /// fn from(value: u64) -> Self { /// Self(value) /// } /// } /// /// nix_deserialize_remote!(#[nix(from="u64")] MyU64); /// ``` #[proc_macro] pub fn nix_deserialize_remote(item: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(item as RemoteInput); let crate_path = parse_quote!(::nix_compat); de::expand_nix_deserialize_remote(crate_path, &input) .unwrap_or_else(syn::Error::into_compile_error) .into() } /// Macro to implement `NixSerialize` on a type. /// Sometimes you can't use the deriver to implement `NixSerialize` /// (like when dealing with types in Rust standard library) but don't want /// to implement it yourself. So this macro can be used for those situations /// where you would derive using `#[nix(display)]`, `#[nix(display = "path")]`, /// `#[nix(store_dir_display)]`, `#[nix(into = "IntoType")]` or /// `#[nix(try_into = "IntoType")]` if you could. /// /// #### Example /// /// ```rust /// # use nix_compat_derive::nix_serialize_remote; /// # /// #[derive(Clone)] /// struct MyU64(u64); /// /// impl From for u64 { /// fn from(value: MyU64) -> Self { /// value.0 /// } /// } /// /// nix_serialize_remote!(#[nix(into="u64")] MyU64); /// ``` #[proc_macro] pub fn nix_serialize_remote(item: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(item as RemoteInput); let crate_path = parse_quote!(::nix_compat); ser::expand_nix_serialize_remote(crate_path, &input) .unwrap_or_else(syn::Error::into_compile_error) .into() }