Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ exclude = [

[workspace.package]
authors = ["Antonio Yang <yanganto@gmail.com>"]
version = "0.12.6"
version = "0.12.7"
edition = "2021"
categories = ["development-tools"]
keywords = ["struct", "patch", "macro", "derive", "overlay"]
Expand Down
165 changes: 93 additions & 72 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion complex-example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ struct-patch = { path = "../lib", features = ["catalyst"] }

[workspace.package]
authors = ["Antonio Yang <yanganto@gmail.com>"]
version = "0.12.6"
version = "0.12.7"
edition = "2021"
categories = ["development-tools"]
keywords = ["struct", "patch", "macro", "derive", "overlay"]
Expand Down
3 changes: 3 additions & 0 deletions complex-example/catalyst/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ substrate = { path = "../substrate" }
serde = { version = "1", features = ["derive"] }
toml = "1.1.2+spec-1.1.0"

[features]
unsafe = ["struct-patch/unsafe"]

[build-dependencies]
struct-patch.workspace = true
substrate = { path = "../substrate" }
1 change: 1 addition & 0 deletions derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ op = []
merge = []
nesting = []
catalyst = [ "syn-serde" ]
unsafe = []

[dev-dependencies]
pretty_assertions_sorted = "1.2.3"
159 changes: 130 additions & 29 deletions derive/src/catalyst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,40 @@ impl Catalyst {
catalyst_fields.push(Field::from_cat_ast(field.clone()));
}

#[cfg(feature = "unsafe")]
let complex_ident = Ident::new("complex", Span::call_site());
#[cfg(feature = "unsafe")]
let catalyst_ident = Ident::new("catalyst", Span::call_site());

#[cfg(feature = "unsafe")]
let catalyst_ptr_read_fields = catalyst_fields
.iter()
.map(|f| f.to_ptr_read_stream(&complex_ident))
.collect::<Result<Vec<_>>>()?;

#[cfg(feature = "unsafe")]
let substrate_ptr_read_fields = substrate_fields
.iter()
.map(|f| f.to_ptr_read_arg_stream(&complex_ident))
.collect::<Result<Vec<_>>>()?;

#[cfg(feature = "unsafe")]
let catalyst_bind_ptr_read_fields = catalyst_fields
.iter()
.map(|f| f.to_ptr_read_stream(&catalyst_ident))
.collect::<Result<Vec<_>>>()?;

let complex_fields = raw_complex_fields
.iter()
.map(|f| f.to_token_stream(*keep_field_attribute, override_field_attributes, exclude_field_attributes))
.collect::<Result<Vec<_>>>()?;

#[cfg(not(feature = "unsafe"))]
let unpack_complex_fields = raw_complex_fields
.iter()
.map(|f| f.to_unpack_stream())
.collect::<Result<Vec<_>>>()?;

#[cfg(not(feature = "unsafe"))]
let catalyst_fields = catalyst_fields
.iter()
.map(|f| f.to_unpack_stream())
Expand All @@ -104,44 +128,93 @@ impl Catalyst {
}
})
.collect::<Vec<_>>();
let catalyst_impl = quote! {
#[automatically_derived]
impl struct_patch::traits::Catalyst < #substrate_name, #complex_struct_name > for #struct_name {
fn bind(self, s: #substrate_name) -> #complex_struct_name {
let (
#(#substrate_fields)*
) = s.__substrate_unpack();
let #struct_name {
#(#catalyst_fields)*
} = self;
#complex_struct_name {
#(#unpack_complex_fields)*

let catalyst_impl = {
#[cfg(feature = "unsafe")]
quote! {
#[automatically_derived]
impl struct_patch::traits::Catalyst < #substrate_name, #complex_struct_name > for #struct_name {
fn bind(self, s: #substrate_name) -> #complex_struct_name {
let (
#(#substrate_fields)*
) = s.__substrate_unpack();
let catalyst = std::mem::ManuallyDrop::new(self);
unsafe {
#complex_struct_name {
#(#substrate_fields)*
#(#catalyst_bind_ptr_read_fields)*
}
}
}
}
}
#[cfg(not(feature = "unsafe"))]
quote! {
#[automatically_derived]
impl struct_patch::traits::Catalyst < #substrate_name, #complex_struct_name > for #struct_name {
fn bind(self, s: #substrate_name) -> #complex_struct_name {
let (
#(#substrate_fields)*
) = s.__substrate_unpack();
let #struct_name {
#(#catalyst_fields)*
} = self;
#complex_struct_name {
#(#unpack_complex_fields)*
}
}
}
}
};

let decouple_impl = {
#[cfg(feature = "unsafe")]
quote! {
#[automatically_derived]
impl struct_patch::traits::Complex < #struct_name, #substrate_name > for #complex_struct_name {
fn decouple(self) -> (#struct_name, #substrate_name) {
let complex = std::mem::ManuallyDrop::new(self);
unsafe {
(
#struct_name {
#(#catalyst_ptr_read_fields)*
},
#substrate_name::__substrate_new(
#(#substrate_ptr_read_fields)*
)
)
}
}
}
}
#[cfg(not(feature = "unsafe"))]
quote! {
#[automatically_derived]
impl struct_patch::traits::Complex < #struct_name, #substrate_name > for #complex_struct_name {
fn decouple(self) -> (#struct_name, #substrate_name) {
let #complex_struct_name {
#(#unpack_complex_fields)*
} = self;
(
#struct_name {
#(#catalyst_fields)*
},
#substrate_name::__substrate_new(
#(#substrate_fields)*
)
)
}
}
}
};

let complex_impl = quote! {
#(#mapped_attributes)*
#visibility struct #complex_struct_name #generics {
#(#complex_fields)*
}

#[automatically_derived]
impl struct_patch::traits::Complex < #struct_name, #substrate_name > for #complex_struct_name {
fn decouple(self) -> (#struct_name, #substrate_name) {
let #complex_struct_name {
#(#unpack_complex_fields)*
} = self;
(
#struct_name {
#(#catalyst_fields)*
},
#substrate_name::__substrate_new(
#(#substrate_fields)*
)
)
}
}
#decouple_impl
};

Ok(quote! {
Expand Down Expand Up @@ -348,6 +421,34 @@ impl Field {
})
}

/// Generate the token stream for reading a field via ptr::read for struct construction
#[cfg(feature = "unsafe")]
pub fn to_ptr_read_stream(&self, source: &Ident) -> Result<TokenStream> {
let Field {
ident,
ty: _,
attributes: _,
attrs: _,
} = self;
Ok(quote! {
#ident: std::ptr::read(&#source.#ident),
})
}

/// Generate the token stream for reading a field via ptr::read as a function argument
#[cfg(feature = "unsafe")]
pub fn to_ptr_read_arg_stream(&self, source: &Ident) -> Result<TokenStream> {
let Field {
ident,
ty: _,
attributes: _,
attrs: _,
} = self;
Ok(quote! {
std::ptr::read(&#source.#ident),
})
}

/// Parse the Catalyst struct field
pub fn from_cat_ast(
syn::Field {
Expand Down
95 changes: 71 additions & 24 deletions derive/src/substrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,76 @@ impl Substrate {

let active_site = json::to_string(fields);

let substrate_impl_inner = {
#[cfg(feature = "unsafe")]
{
quote! {
#[automatically_derived]
impl #struct_name {
#[allow(clippy::too_many_arguments)]
/// Support `decouple` of Complex with private fields, a new function generated by Substrate macro
pub fn __substrate_new(
#( #field_idents: #field_tys, )*
) -> #struct_name {
unsafe {
use std::mem::MaybeUninit;
let mut substrate = MaybeUninit::<#struct_name>::uninit();
#( std::ptr::write(
std::ptr::addr_of_mut!((*substrate.as_mut_ptr()).#field_idents),
#field_idents,
); )*
substrate.assume_init()
}
}

#[allow(clippy::too_many_arguments)]
/// Support `bind` of Catalyst with private fields, an unpack function generated by Substrate macro
pub fn __substrate_unpack(
self
) -> (
#( #field_tys, )*
) {
let substrate = std::mem::ManuallyDrop::new(self);
unsafe {
(
#( std::ptr::read(&substrate.#field_idents), )*
)
}
}
}
}
}
#[cfg(not(feature = "unsafe"))]
{
quote! {
#[automatically_derived]
impl #struct_name {
#[allow(clippy::too_many_arguments)]
/// Support `decouple` of Complex with private fields, a new function generated by Substrate macro
pub fn __substrate_new(
#( #field_idents: #field_tys, )*
) -> #struct_name {
#struct_name {
#( #field_idents, )*
}
}

#[allow(clippy::too_many_arguments)]
/// Support `bind` of Catalyst with private fields, an unpack function generated by Substrate macro
pub fn __substrate_unpack(
self
) -> (
#( #field_tys, )*
) {
(
#( self.#field_idents, )*
)
}
}
}
}
};

Ok(quote! {
#[automatically_derived]
impl struct_patch::traits::Substrate for #struct_name {
Expand All @@ -36,30 +106,7 @@ impl Substrate {
}
}

#[automatically_derived]
impl #struct_name {
#[allow(clippy::too_many_arguments)]
/// Support `decouple` of Complex with private fields, a new function generated by Substrate macro
pub fn __substrate_new(
#( #field_idents: #field_tys, )*
) -> #struct_name {
#struct_name {
#( #field_idents, )*
}
}

#[allow(clippy::too_many_arguments)]
/// Support `bind` of Catalyst with private fields, an unpack function generated by Substrate macro
pub fn __substrate_unpack(
self
) -> (
#( #field_tys, )*
) {
(
#( self.#field_idents, )*
)
}
}
#substrate_impl_inner
})
}
/// Parse the substrate struct
Expand Down
3 changes: 3 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
cd complex-example
cargo test -p substrate
cargo test -p catalyst

echo "Run catatyst test with unsafe features"
cargo test -p catalyst --features unsafe
'';
in
with pkgs;
Expand Down
5 changes: 4 additions & 1 deletion lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ readme.workspace = true
rust-version.workspace = true

[dependencies]
struct-patch-derive = { version = "=0.12.6", path = "../derive" }
struct-patch-derive = { version = "=0.12.7", path = "../derive" }

[dev-dependencies]
serde_json = "1.0"
Expand Down Expand Up @@ -46,3 +46,6 @@ keep_none = ["option"]
catalyst = [
"struct-patch-derive/catalyst"
]
unsafe = [
"struct-patch-derive/unsafe"
]
2 changes: 1 addition & 1 deletion no-std-examples/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "no-std-examples"
authors = ["Antonio Yang <yanganto@gmail.com>"]
version = "0.12.6"
version = "0.12.7"
edition = "2021"
license = "MIT"

Expand Down