refactor(merge): use existing merge create

This commit is contained in:
2025-07-10 14:49:25 +03:00
parent 8b82d09318
commit b341b7a661
15 changed files with 230 additions and 714 deletions

View File

File diff suppressed because one or more lines are too long

107
Cargo.lock generated
View File

@@ -17,6 +17,19 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"getrandom 0.3.3",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -301,15 +314,6 @@ dependencies = [
"powerfmt",
]
[[package]]
name = "derive_macro"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "derive_more"
version = "2.0.1"
@@ -933,6 +937,28 @@ version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "merge"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e520ba58faea3487f75df198b1d079644ec226ea3b0507d002c6fa4b8cf93a"
dependencies = [
"merge_derive",
"num-traits",
]
[[package]]
name = "merge_derive"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8f8ce6efff81cbc83caf4af0905c46e58cb46892f63ad3835e81b47eaf7968"
dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
@@ -1083,6 +1109,28 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "proc-macro-error2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
@@ -1859,6 +1907,7 @@ version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@@ -1887,6 +1936,23 @@ dependencies = [
"syn",
]
[[package]]
name = "tracing-bunyan-formatter"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d637245a0d8774bd48df6482e086c59a8b5348a910c3b0579354045a9d82411"
dependencies = [
"ahash",
"log",
"serde",
"serde_json",
"time",
"tracing",
"tracing-core",
"tracing-log 0.1.4",
"tracing-subscriber",
]
[[package]]
name = "tracing-core"
version = "0.1.34"
@@ -1907,6 +1973,17 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "tracing-log"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
@@ -1933,7 +2010,7 @@ dependencies = [
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
"tracing-log 0.2.0",
]
[[package]]
@@ -1958,8 +2035,8 @@ version = "0.1.0"
dependencies = [
"color-eyre",
"crossterm 0.29.0",
"derive_macro",
"dirs",
"merge",
"ratatui",
"serde",
"thiserror 2.0.12",
@@ -1967,6 +2044,8 @@ dependencies = [
"toml",
"tracing",
"tracing-appender",
"tracing-bunyan-formatter",
"tracing-log 0.2.0",
"tracing-subscriber",
"transmission-rpc",
"url",
@@ -2042,6 +2121,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "want"
version = "0.3.1"

View File

@@ -8,15 +8,17 @@ edition = "2021"
[dependencies]
color-eyre = "0.6"
crossterm = "0.29"
ratatui = { version = "0.29" }
tokio = { version = "1", features = ["macros", "rt-multi-thread", "fs"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
thiserror = "2.0"
tracing-appender = "0.2"
serde = { version = "1.0", features = ["derive"] }
toml = "0.9"
dirs = "6.0"
merge = "0.2"
ratatui = { version = "0.29" }
serde = { version = "1.0", features = ["derive"] }
thiserror = "2.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "fs"] }
toml = "0.9"
tracing = "0.1"
tracing-appender = "0.2"
tracing-bunyan-formatter = { version = "0.3", default-features = false }
tracing-log = "0.2.0"
tracing-subscriber = { version = "0.3", features = ["registry", "env-filter"] }
transmission-rpc = "0.5"
url = "2.5"
derive_macro = { path = "derive_macro" }

View File

@@ -21,3 +21,8 @@ highlight_foreground = "black"
warning_foreground = "yellow"
info_foreground = "blue"
error_foreground = "red"
[log]
traxor = "info"
ratatui = "warn"
transmission_rpc = "warn"

297
derive_macro/Cargo.lock generated
View File

@@ -1,297 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "derive_macro"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
"trybuild",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "glob"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]]
name = "hashbrown"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
[[package]]
name = "indexmap"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
dependencies = [
"serde",
]
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "target-triple"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790"
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "toml"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"toml_write",
"winnow",
]
[[package]]
name = "toml_write"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "trybuild"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c9bf9513a2f4aeef5fdac8677d7d349c79fdbcc03b9c86da6e9d254f1e43be2"
dependencies = [
"glob",
"serde",
"serde_derive",
"serde_json",
"target-triple",
"termcolor",
"toml",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "winapi-util"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
dependencies = [
"memchr",
]

View File

@@ -1,15 +0,0 @@
[package]
name = "derive_macro"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["proc-macro"]
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", features = ["full", "extra-traits"] }
[dev-dependencies]
trybuild = "1.0"

View File

@@ -1,10 +0,0 @@
mod merge;
use proc_macro::TokenStream;
use syn::{DeriveInput, parse_macro_input};
#[proc_macro_derive(Merge)]
pub fn merge_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
merge::impl_merge_derive(input)
}

View File

@@ -1,58 +0,0 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, Fields, Type};
pub fn impl_merge_derive(input: DeriveInput) -> TokenStream {
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let fields = match input.data {
Data::Struct(data) => match data.fields {
Fields::Named(fields) => fields.named,
_ => unimplemented!("Only named fields are supported for Merge derive macro"),
},
_ => unimplemented!("Only structs are supported for Merge derive macro"),
};
let merge_logic = fields.iter().map(|field| {
let field_name = &field.ident;
let field_type = &field.ty;
// Check if the field is an Option<T>
if let Type::Path(type_path) = field_type {
if let Some(segment) = type_path.path.segments.last() {
if segment.ident == "Option" {
// This is an Option<T> field
return quote! {
if let Some(o_val) = other.#field_name {
if let Some(s_val) = self.#field_name.as_mut() {
// If both are Some, attempt to merge recursively
s_val.merge(o_val);
} else {
// If self is None, take the other's Some value
self.#field_name = Some(o_val);
}
}
// If other is None, self remains unchanged
};
}
}
}
// For non-Option fields, attempt to merge recursively
quote! {
self.#field_name.merge(other.#field_name);
}
});
let expanded = quote! {
impl #impl_generics Merge for #name #ty_generics #where_clause {
fn merge(&mut self, other: Self) {
#(#merge_logic)*
}
}
};
expanded.into()
}

View File

@@ -1,14 +1,18 @@
use crate::merge::Merge;
use derive_macro::Merge;
use merge::Merge;
use ratatui::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Merge)]
pub struct ColorsConfig {
#[merge(strategy = merge::option::overwrite_none)]
pub highlight_background: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub highlight_foreground: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub warning_foreground: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub info_foreground: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub error_foreground: Option<String>,
}

View File

@@ -1,23 +1,37 @@
use crate::merge::Merge;
use derive_macro::Merge;
use merge::Merge;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Merge)]
pub struct KeybindsConfig {
#[merge(strategy = merge::option::overwrite_none)]
pub quit: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub next_tab: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub prev_tab: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub next_torrent: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub prev_torrent: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub switch_tab_1: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub switch_tab_2: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub switch_tab_3: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub toggle_torrent: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub toggle_all: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub delete: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub delete_force: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub select: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub toggle_help: Option<String>,
#[merge(strategy = merge::option::overwrite_none)]
pub move_torrent: Option<String>,
}

View File

@@ -1,11 +1,10 @@
mod colors;
mod keybinds;
use crate::merge::Merge;
use color_eyre::Result;
use colors::ColorsConfig;
use derive_macro::Merge;
use keybinds::KeybindsConfig;
use merge::Merge;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

View File

@@ -3,6 +3,5 @@ pub mod config;
pub mod event;
pub mod handler;
pub mod log;
pub mod merge;
pub mod tui;
pub mod ui;

View File

@@ -1,21 +0,0 @@
pub trait Merge {
fn merge(&mut self, other: Self);
}
impl Merge for String {
fn merge(&mut self, other: Self) {
*self = other;
}
}
impl Merge for u32 {
fn merge(&mut self, other: Self) {
*self = other;
}
}
impl Merge for bool {
fn merge(&mut self, other: Self) {
*self = other;
}
}

View File

@@ -1,286 +0,0 @@
use derive_macro::Merge;
use traxor::merge::Merge;
#[derive(Debug, PartialEq, Merge)]
struct NestedStruct {
value: u32,
name: Option<String>,
}
#[derive(Debug, PartialEq, Merge)]
struct TestStruct {
a: u32,
b: Option<String>,
c: Option<bool>,
nested: NestedStruct,
optional_nested: Option<NestedStruct>,
}
#[test]
fn test_merge_basic_fields() {
let mut s1 = TestStruct {
a: 1,
b: Some("hello".to_string()),
c: None,
nested: NestedStruct {
value: 10,
name: Some("original".to_string()),
},
optional_nested: None,
};
let s2 = TestStruct {
a: 2,
b: Some("world".to_string()),
c: Some(true),
nested: NestedStruct {
value: 20,
name: Some("new".to_string()),
},
optional_nested: Some(NestedStruct {
value: 30,
name: Some("optional".to_string()),
}),
};
s1.merge(s2);
assert_eq!(s1.a, 2);
assert_eq!(s1.b, Some("world".to_string()));
assert_eq!(s1.c, Some(true));
assert_eq!(s1.nested.value, 20);
assert_eq!(s1.nested.name, Some("new".to_string()));
assert_eq!(
s1.optional_nested,
Some(NestedStruct {
value: 30,
name: Some("optional".to_string())
})
);
}
#[test]
fn test_merge_option_none_other() {
let mut s1 = TestStruct {
a: 1,
b: Some("hello".to_string()),
c: Some(false),
nested: NestedStruct {
value: 10,
name: Some("original".to_string()),
},
optional_nested: Some(NestedStruct {
value: 100,
name: Some("existing".to_string()),
}),
};
let s2 = TestStruct {
a: 2,
b: None,
c: None,
nested: NestedStruct {
value: 20,
name: None,
},
optional_nested: None,
};
s1.merge(s2);
assert_eq!(s1.a, 2);
assert_eq!(s1.b, Some("hello".to_string())); // Should remain Some("hello")
assert_eq!(s1.c, Some(false)); // Should remain Some(false)
assert_eq!(s1.nested.value, 20);
assert_eq!(s1.nested.name, Some("original".to_string())); // Should remain original
assert_eq!(
s1.optional_nested,
Some(NestedStruct {
value: 100,
name: Some("existing".to_string())
})
); // Should remain existing
}
#[test]
fn test_merge_option_some_other() {
let mut s1 = TestStruct {
a: 1,
b: None,
c: None,
nested: NestedStruct {
value: 10,
name: None,
},
optional_nested: None,
};
let s2 = TestStruct {
a: 2,
b: Some("world".to_string()),
c: Some(true),
nested: NestedStruct {
value: 20,
name: Some("new".to_string()),
},
optional_nested: Some(NestedStruct {
value: 30,
name: Some("optional".to_string()),
}),
};
s1.merge(s2);
assert_eq!(s1.a, 2);
assert_eq!(s1.b, Some("world".to_string()));
assert_eq!(s1.c, Some(true));
assert_eq!(s1.nested.value, 20);
assert_eq!(s1.nested.name, Some("new".to_string()));
assert_eq!(
s1.optional_nested,
Some(NestedStruct {
value: 30,
name: Some("optional".to_string())
})
);
}
#[test]
fn test_merge_nested_struct_with_none_name() {
let mut s1 = TestStruct {
a: 1,
b: None,
c: None,
nested: NestedStruct {
value: 10,
name: Some("original".to_string()),
},
optional_nested: None,
};
let s2 = TestStruct {
a: 2,
b: None,
c: None,
nested: NestedStruct {
value: 20,
name: None,
},
optional_nested: None,
};
s1.merge(s2);
assert_eq!(s1.nested.value, 20);
assert_eq!(s1.nested.name, Some("original".to_string())); // Should remain original
}
#[test]
fn test_merge_optional_nested_struct_some_to_some() {
let mut s1 = TestStruct {
a: 1,
b: None,
c: None,
nested: NestedStruct {
value: 10,
name: None,
},
optional_nested: Some(NestedStruct {
value: 100,
name: Some("existing".to_string()),
}),
};
let s2 = TestStruct {
a: 2,
b: None,
c: None,
nested: NestedStruct {
value: 20,
name: None,
},
optional_nested: Some(NestedStruct {
value: 200,
name: Some("new_optional".to_string()),
}),
};
s1.merge(s2);
assert_eq!(
s1.optional_nested,
Some(NestedStruct {
value: 200,
name: Some("new_optional".to_string())
})
);
}
#[test]
fn test_merge_optional_nested_struct_none_to_some() {
let mut s1 = TestStruct {
a: 1,
b: None,
c: None,
nested: NestedStruct {
value: 10,
name: None,
},
optional_nested: None,
};
let s2 = TestStruct {
a: 2,
b: None,
c: None,
nested: NestedStruct {
value: 20,
name: None,
},
optional_nested: Some(NestedStruct {
value: 200,
name: Some("new_optional".to_string()),
}),
};
s1.merge(s2);
assert_eq!(
s1.optional_nested,
Some(NestedStruct {
value: 200,
name: Some("new_optional".to_string())
})
);
}
#[test]
fn test_merge_optional_nested_struct_some_to_none() {
let mut s1 = TestStruct {
a: 1,
b: None,
c: None,
nested: NestedStruct {
value: 10,
name: None,
},
optional_nested: Some(NestedStruct {
value: 100,
name: Some("existing".to_string()),
}),
};
let s2 = TestStruct {
a: 2,
b: None,
c: None,
nested: NestedStruct {
value: 20,
name: None,
},
optional_nested: None,
};
s1.merge(s2);
assert_eq!(
s1.optional_nested,
Some(NestedStruct {
value: 100,
name: Some("existing".to_string())
})
); // Should remain existing
}