about summary refs log tree commit diff
path: root/tvix/nix-compat-derive/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/nix-compat-derive/src/lib.rs')
-rw-r--r--tvix/nix-compat-derive/src/lib.rs175
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()
+}