Crates.io | struct-merge |
lib.rs | struct-merge |
version | 0.1.1 |
source | src |
created_at | 2021-12-30 16:56:43.929506 |
updated_at | 2022-04-07 08:18:45.45504 |
description | Automatically generated code for various structs merge-strategies. |
homepage | https://github.com/nukesor/struct-merge |
repository | https://github.com/nukesor/struct-merge |
max_upload_size | |
id | 505425 |
size | 16,143 |
This project is superseded by inter-struct.
Consider switching to that library, as this library won't get any updates.
Generate code for merging various structs.
This crate provides two proc-macros to generate two very similar traits.
/// Merge another struct into Self whilst consuming it.
///
/// The other trait is named `StructMergeRef` and merges other structs by reference.
pub trait StructMerge<Src> {
/// Merge the given struct into self.
fn merge(&mut self, src: Src);
/// Nearly the same as `merge`, but any `Self::Option<T>` fields will only get merged, if the
/// value of the field is `None`.
fn merge_soft(&mut self, src: Src);
}
Please read the known caveats section before using this crate!
This example shows a simple usage of the struct_merge
macro.
structs.rs
: The structs that will be used in this example.
use struct_merge::struct_merge;
/// The target struct we'll merge into.
pub struct Target {
pub normal: String,
pub optional: Option<String>,
/// This field won't be touched as the macro cannot find a
/// respective `ignored` field in the `Mixed` struct.
pub ignored: Option<String>,
}
/// A struct with both an identical and an optional field type.
/// Note that the path to `Target` must always a fully qualifying path.
#[struct_merge(crate::structs::Target)]
pub struct Mixed {
pub normal: String,
pub optional: Option<Option<String>>,
}
lib.rs
use struct_merge::prelude::*;
mod structs;
use structs::{Target, Mixed};
fn main() {
let mut target = Target {
normal: "target".to_string(),
optional: Some("target".to_string()),
ignored: "target".to_string(),
};
let mixed = Mixed {
/// Has the same type as Target::normal
normal: "mixed".to_string(),
/// Wraps Target::optional in an Option
optional: Some(Some("mixed".to_string())),
};
// Merge the `Mixed` struct into target.
target.merge(mixed);
// You can also call this:
// mixed.merge_into(target);
assert_eq!(target.normal, "mixed".to_string());
assert_eq!(target.optional, Some("mixed".to_string()));
assert_eq!(target.ignored, "target".to_string());
}
The following will explain the merge behavior of a single field on the target struct.
The name of the target field is test
.
merge
and merge_ref
struct Src {
test: T
}
struct Target {
test: T
}
This will simply merge src.test
into target.test
:
target.test = src.test
struct Src {
test: T
}
struct Target {
test: Option<T>
}
This will wrap src.test
into an Option
and merge it into target.test
:
target.test = Some(src.test);
struct Src {
test: Option<T>
}
struct Target {
test: T
}
This will only merge src.test
into target.test
if src.test
is Some
: \
if let Some(value) = src.test {
target.test = value;
}
merge_soft
and merge_ref_soft
target.test
is not OptionalAs long as a target field is not optional it won't be touched!
struct Src {
test: T
}
struct Target {
test: Option<T>
}
This will wrap src.test
into an Option
and merge it into target.test
but only if target.test
is None
: \
if target.test.is_none() {
target.test = Some(src.test);
}
struct Src {
test: Option<T>
}
struct Target {
test: Option<T>
}
This will only merge src.test
into target.test
if target.test
is None
: \
if target.test.is_none() {
target.test = src.test;
}
There is no easy way to do path or type resolution during the procedural macro stage.
For this reason, this crate might not work with your project's structure.
However, as we're creating safe and valid Rust code the compiler will thrown an error if any problems arise.
When using the normal merge
functions, the worst thing that might happen is that this crate won't compile.
However, when using obscured types such as type aliases, the merge_soft
functions won't detect Option
s properly and might merge values even though they're already Some
!
These are problems that can probably be solved but they're non-trivial.
lib.rs
.mod {}
block in file.src
.
We would have to check the environment and possibly parse the Cargo.toml
.Box<dyn T>
and Box<dyn S>
but both S
and T
have the Clone
trait bound.These are problems that are either impossible to solve or very infeasible. For instance, something infeasible would be to parse all files and to do a full type resolution of a given crate. That would be a job for the compiler in later stages.
type test = Option<String>
won't be detected as an Option.
The current check for Option
fields is a literal check for the Option
token.