diff options
Diffstat (limited to 'tvix/nix-compat-derive/src/lib.rs')
-rw-r--r-- | tvix/nix-compat-derive/src/lib.rs | 175 |
1 files changed, 166 insertions, 9 deletions
diff --git a/tvix/nix-compat-derive/src/lib.rs b/tvix/nix-compat-derive/src/lib.rs index 89735cadf315..394473b1cbf8 100644 --- a/tvix/nix-compat-derive/src/lib.rs +++ b/tvix/nix-compat-derive/src/lib.rs @@ -6,7 +6,11 @@ //! 1. [`#[nix(from_str)]`](#nixfrom_str) //! 2. [`#[nix(from = "FromType")]`](#nixfrom--fromtype) //! 3. [`#[nix(try_from = "FromType")]`](#nixtry_from--fromtype) -//! 4. [`#[nix(crate = "...")]`](#nixcrate--) +//! 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) @@ -17,20 +21,21 @@ //! ## Overview //! //! This crate contains derive macros and function-like macros for implementing -//! `NixDeserialize` with less boilerplate. +//! `NixDeserialize` and `NixSerialize` with less boilerplate. //! //! ### Examples +//! //! ```rust -//! # use nix_compat_derive::NixDeserialize; +//! # use nix_compat_derive::{NixDeserialize, NixSerialize}; //! # -//! #[derive(NixDeserialize)] +//! #[derive(NixDeserialize, NixSerialize)] //! struct Unnamed(u64, String); //! ``` //! //! ```rust -//! # use nix_compat_derive::NixDeserialize; +//! # use nix_compat_derive::{NixDeserialize, NixSerialize}; //! # -//! #[derive(NixDeserialize)] +//! #[derive(NixDeserialize, NixSerialize)] //! struct Fields { //! number: u64, //! message: String, @@ -38,9 +43,9 @@ //! ``` //! //! ```rust -//! # use nix_compat_derive::NixDeserialize; +//! # use nix_compat_derive::{NixDeserialize, NixSerialize}; //! # -//! #[derive(NixDeserialize)] +//! #[derive(NixDeserialize, NixSerialize)] //! struct Ignored; //! ``` //! @@ -64,7 +69,7 @@ //! #[derive(NixDeserialize)] //! #[nix(crate="nix_compat")] // <-- This is also a container attribute //! enum E { -//! #[nix(version="..=9")] // <-- This is a variant attribute +//! #[nix(version="..10")] // <-- This is a variant attribute //! A(u64), //! #[nix(version="10..")] // <-- This is also a variant attribute //! B(String), @@ -156,6 +161,114 @@ //! } //! ``` //! +//! ##### `#[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<IntoType>` 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<MyValue> 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<IntoType>` 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<WrongAnswer> for usize { +//! type Error = String; +//! fn try_from(val: WrongAnswer) -> Result<Self, Self::Error> { +//! 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 @@ -175,6 +288,7 @@ //! //! ```rust //! # use nix_compat_derive::NixDeserialize; +//! # //! #[derive(NixDeserialize)] //! enum Testing { //! #[nix(version="..=18")] @@ -260,6 +374,7 @@ 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 { @@ -270,6 +385,15 @@ pub fn derive_nix_deserialize(item: TokenStream) -> TokenStream { .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 @@ -301,3 +425,36 @@ pub fn nix_deserialize_remote(item: TokenStream) -> TokenStream { .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<MyU64> 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() +} |