Expand description
§Using derive
§Overview
This crate contains derive macros and function-like macros for implementing
NixDeserialize
and NixSerialize
with less boilerplate.
§Examples
#[derive(NixDeserialize, NixSerialize)]
struct Unnamed(u64, String);
#[derive(NixDeserialize, NixSerialize)]
struct Fields {
number: u64,
message: String,
};
#[derive(NixDeserialize, NixSerialize)]
struct Ignored;
§Attributes
To customize the derived trait implementations you can add attributes to containers, fields and variants.
#[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
#[derive(NixDeserialize)]
#[nix(from_str)]
struct MyString(String);
impl std::str::FromStr for MyString {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
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<FromType>
and
FromType
must implement NixDeserialize
.
§Example
#[derive(NixDeserialize)]
#[nix(from="usize")]
struct MyValue(usize);
impl From<usize> 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<FromType>
and
FromType
must implement NixDeserialize
.
The error returned from try_from
also needs to implement Display
.
§Example
#[derive(NixDeserialize)]
#[nix(try_from="usize")]
struct WrongAnswer(usize);
impl TryFrom<usize> for WrongAnswer {
type Error = String;
fn try_from(val: usize) -> Result<Self, Self::Error> {
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<IntoType>
and Clone
and IntoType
must implement NixSerialize
.
§Example
#[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
#[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
#[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
#[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
#[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
#[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")]
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")]
.
§Example
#[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")]
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
#[derive(NixDeserialize)]
struct Field {
number: u64,
#[nix(version="..20", default="missing_string")]
messsage: String,
}
fn missing_string() -> String {
"missing string".to_string()
}
Modules§
Macros§
- Macro to implement
NixDeserialize
on a type. Sometimes you can’t use the deriver to implementNixDeserialize
(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. - Macro to implement
NixSerialize
on a type. Sometimes you can’t use the deriver to implementNixSerialize
(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.