From 1eb6dce9d7c932a8fb07a7fc4b66e18666453ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 14:26:54 +0100 Subject: [PATCH 01/37] Add clippy lints --- Cargo.toml | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++ clippy.toml | 8 +++++++ 2 files changed, 72 insertions(+) create mode 100644 clippy.toml diff --git a/Cargo.toml b/Cargo.toml index d94ea9b..9b0a240 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,3 +38,67 @@ specialization = [] [[bin]] name = "cp2cp" required-features = ["cp2cp"] + +[lints.clippy] +pedantic = {priority = -1, level = "warn"} + +allow_attributes = "warn" +allow_attributes_without_reason = "warn" +box_collection = "warn" +dbg_macro = "warn" +enum_variant_names = "warn" +equatable_if_let = "warn" +exit = "warn" +indexing_slicing = "warn" +infinite_loop = "warn" +iter_on_single_items = "warn" +large_types_passed_by_value = "warn" +let_underscore_must_use = "warn" +linkedlist = "warn" +map_err_ignore = "warn" +missing_assert_message = "warn" +needless_pass_by_ref_mut = "warn" +option-if-let-else = "warn" +option_option = "warn" +or_fun_call = "warn" +owned_cow = "warn" +panic = "warn" +print_stderr = "warn" +print_stdout = "warn" +rc_buffer = "warn" +rc_mutex = "warn" +redundant_allocation = "warn" +redundant_clone = "warn" +redundant_type_annotations = "warn" +ref_option = "warn" +rest_pat_in_fully_bound_structs = "warn" +return_and_then = "warn" +string_lit_as_bytes = "warn" +string_slice = "warn" +trivially_copy_pass_by_ref = "warn" +try_err = "warn" +undocumented_unsafe_blocks = "warn" +unimplemented = "warn" +unnecessary_box_returns = "warn" +unnecessary_wraps = "warn" +unneeded_field_pattern = "warn" +unused_self = "warn" +unwrap_used = "warn" +upper_case_acronyms = "warn" +useless_let_if_seq = "warn" +vec_box = "warn" +wrong_self_convention = "warn" + +doc_markdown = "allow" +expect_used = "allow" +explicit_iter_loop = "allow" +float-cmp = "allow" +format_push_string = "allow" +items_after_statements = "allow" +manual_string_new = "allow" +missing_errors_doc = "allow" +missing_panics_doc = "allow" +must_use_candidate = "allow" +should_panic_without_expect = "allow" +todo = "allow" +too_many_lines = "allow" diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..f01facb --- /dev/null +++ b/clippy.toml @@ -0,0 +1,8 @@ +allow-expect-in-consts = true +allow-expect-in-tests = true +allow-indexing-slicing-in-tests = true +allow-one-hash-in-raw-strings = true +allow-panic-in-tests = true +allow-print-in-tests = true +allow-unwrap-in-tests = true +avoid-breaking-exported-api = false From da307769711353493ca8f52ee8c41d80d1162e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Mon, 2 Feb 2026 23:56:02 +0100 Subject: [PATCH 02/37] Use assert! instead of if-then-panic --- libshvproto-macros/src/lib.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/libshvproto-macros/src/lib.rs b/libshvproto-macros/src/lib.rs index 6f16adf..e5d99d8 100644 --- a/libshvproto-macros/src/lib.rs +++ b/libshvproto-macros/src/lib.rs @@ -235,9 +235,7 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream { }; match &variant.fields { syn::Fields::Unnamed(variant_types) => { - if variant_types.unnamed.len() != 1 { - panic!("Only single element variant tuples are supported for FromRpcValue"); - } + assert!(variant_types.unnamed.len() == 1, "Only single element variant tuples are supported for FromRpcValue"); let source_variant_type = &variant_types.unnamed.first().expect("No tuple elements").ty; let deref_code = quote!((*x)); let unbox_code = quote!((x.as_ref().clone())); @@ -431,9 +429,7 @@ pub fn derive_to_rpcvalue(item: TokenStream) -> TokenStream { let variant_ident = &variant.ident; match &variant.fields { syn::Fields::Unnamed(variant_types) => { - if variant_types.unnamed.len() != 1 { - panic!("Only single element variant tuples are supported for ToRpcValue"); - } + assert!(variant_types.unnamed.len() == 1, "Only single element variant tuples are supported for ToRpcValue"); match_arms_ser.extend(quote!{ #struct_identifier::#variant_ident(val) => shvproto::RpcValue::from(val), }); From 43585a645f679568ecdbe7d1253df7d58fc261e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 13:52:13 +0100 Subject: [PATCH 03/37] Remove allows --- src/datetime.rs | 1 - src/decimal.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/datetime.rs b/src/datetime.rs index 8e97841..79da486 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -9,7 +9,6 @@ use chrono::{FixedOffset, NaiveDateTime, Offset}; /// I'm storing whole DateTime in one i64 to keep size_of RpcValue == 24 const TZ_MASK: i64 = 127; pub enum IncludeMilliseconds { - #[allow(dead_code)] Never, Always, WhenNonZero, diff --git a/src/decimal.rs b/src/decimal.rs index 3cfaa57..b35672f 100644 --- a/src/decimal.rs +++ b/src/decimal.rs @@ -82,7 +82,6 @@ impl Decimal { let mut d = decoded.0 as f64; let exp = decoded.1; // We probably don't want to call .cmp() because of performance loss - #[allow(clippy::comparison_chain)] if exp < 0 { for _ in exp .. 0 { d /= 10.; From 4a634171dde44102d1e14f9c8b6b4da58508d95a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 13:59:02 +0100 Subject: [PATCH 04/37] Fix unreadable literals --- src/chainpack.rs | 96 ++++++++++++++++++++++++------------------------ src/cpon.rs | 14 +++---- src/json.rs | 8 ++-- src/textrdwr.rs | 2 +- 4 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/chainpack.rs b/src/chainpack.rs index 6dc9bf5..81dd973 100644 --- a/src/chainpack.rs +++ b/src/chainpack.rs @@ -30,7 +30,7 @@ pub enum PackingSchema { TERM = 255, } -const SHV_EPOCH_MSEC: i64 = 1517529600000; +const SHV_EPOCH_MSEC: i64 = 1_517_529_600_000; pub struct ChainPackWriter<'a, W> where @@ -60,11 +60,11 @@ where fn significant_bits_part_length(num: u64) -> u32 { let mut len = 0; let mut n = num; - if (n & 0xFFFFFFFF00000000) != 0 { + if (n & 0xFFFF_FFFF_0000_0000) != 0 { len += 32; n >>= 32; } - if (n & 0xFFFF0000) != 0 { + if (n & 0xFFFF_0000) != 0 { len += 16; n >>= 16; } @@ -646,22 +646,22 @@ fn test_int() { assert_eq!(chainpack_to_rpcvalue("02").unwrap(), 2_u64.into()); assert_eq!(chainpack_to_rpcvalue("8178").unwrap(), 120_u64.into()); assert_eq!(chainpack_to_rpcvalue("8181FC").unwrap(), 508_u64.into()); - assert_eq!(chainpack_to_rpcvalue("81CFFFFA").unwrap(), 1048570_u64.into()); - assert_eq!(chainpack_to_rpcvalue("81E1FFFFE0").unwrap(), 33554400_u64.into()); - assert_eq!(chainpack_to_rpcvalue("82F3138083FD18A37C").unwrap(), 5489328932823932_i64.into()); - assert_eq!(chainpack_to_rpcvalue("82F47FFFFFFFFFFFFFFF").unwrap(), 9223372036854775807_i64.into()); + assert_eq!(chainpack_to_rpcvalue("81CFFFFA").unwrap(), 1_048_570_u64.into()); + assert_eq!(chainpack_to_rpcvalue("81E1FFFFE0").unwrap(), 33_554_400_u64.into()); + assert_eq!(chainpack_to_rpcvalue("82F3138083FD18A37C").unwrap(), 5_489_328_932_823_932_i64.into()); + assert_eq!(chainpack_to_rpcvalue("82F47FFFFFFFFFFFFFFF").unwrap(), 9_223_372_036_854_775_807_i64.into()); assert_eq!(rpcvalue_to_chainpack(120_u64.into()), "8178"); assert_eq!(rpcvalue_to_chainpack(2_u64.into()), "02"); assert_eq!(rpcvalue_to_chainpack(508_u64.into()), "8181FC"); - assert_eq!(rpcvalue_to_chainpack(1048570_u64.into()), "81CFFFFA"); - assert_eq!(rpcvalue_to_chainpack(33554400_u64.into()), "81E1FFFFE0"); - assert_eq!(rpcvalue_to_chainpack(5489328932823932_i64.into()), "82F3138083FD18A37C"); - assert_eq!(rpcvalue_to_chainpack(9223372036854775807_i64.into()), "82F47FFFFFFFFFFFFFFF"); + assert_eq!(rpcvalue_to_chainpack(1_048_570_u64.into()), "81CFFFFA"); + assert_eq!(rpcvalue_to_chainpack(33_554_400_u64.into()), "81E1FFFFE0"); + assert_eq!(rpcvalue_to_chainpack(5_489_328_932_823_932_i64.into()), "82F3138083FD18A37C"); + assert_eq!(rpcvalue_to_chainpack(9_223_372_036_854_775_807_i64.into()), "82F47FFFFFFFFFFFFFFF"); // Negative int - assert_eq!(chainpack_to_rpcvalue("82E9FFFFE0").unwrap(), (-33554400).into()); - assert_eq!(rpcvalue_to_chainpack((-33554400).into()), "82E9FFFFE0"); + assert_eq!(chainpack_to_rpcvalue("82E9FFFFE0").unwrap(), (-33_554_400).into()); + assert_eq!(rpcvalue_to_chainpack((-33_554_400).into()), "82E9FFFFE0"); } #[test] @@ -736,43 +736,43 @@ fn test_imap() { #[test] fn test_datetime() { - assert_eq!(chainpack_to_rpcvalue("8D04").unwrap(), DateTime::from_epoch_msec_tz(1517529600001, 0).into()); - assert_eq!(chainpack_to_rpcvalue("8D8211").unwrap(), DateTime::from_epoch_msec_tz(1517529600001, 3600).into()); - assert_eq!(chainpack_to_rpcvalue("8DE63DDA02").unwrap(), DateTime::from_epoch_msec_tz(1543708800000, 0).into()); - assert_eq!(chainpack_to_rpcvalue("8DE8A8BFFE").unwrap(), DateTime::from_epoch_msec_tz(1514764800000, 0).into()); - assert_eq!(chainpack_to_rpcvalue("8DE6DC0E02").unwrap(), DateTime::from_epoch_msec_tz(1546300800000, 0).into()); - assert_eq!(chainpack_to_rpcvalue("8DF00E60DC02").unwrap(), DateTime::from_epoch_msec_tz(1577836800000, 0).into()); - assert_eq!(chainpack_to_rpcvalue("8DF015EAF002").unwrap(), DateTime::from_epoch_msec_tz(1609459200000, 0).into()); - assert_eq!(chainpack_to_rpcvalue("8DF061258802").unwrap(), DateTime::from_epoch_msec_tz(1924992000000, 0).into()); - assert_eq!(chainpack_to_rpcvalue("8DF100AC656602").unwrap(), DateTime::from_epoch_msec_tz(2240611200000, 0).into()); - assert_eq!(chainpack_to_rpcvalue("8DF156D74D495F").unwrap(), DateTime::from_epoch_msec_tz(2246004900000, -36900).into()); - assert_eq!(chainpack_to_rpcvalue("8DF301533905E2375D").unwrap(), DateTime::from_epoch_msec_tz(2246004900123, -36900).into()); + assert_eq!(chainpack_to_rpcvalue("8D04").unwrap(), DateTime::from_epoch_msec_tz(1_517_529_600_001, 0).into()); + assert_eq!(chainpack_to_rpcvalue("8D8211").unwrap(), DateTime::from_epoch_msec_tz(1_517_529_600_001, 3600).into()); + assert_eq!(chainpack_to_rpcvalue("8DE63DDA02").unwrap(), DateTime::from_epoch_msec_tz(1_543_708_800_000, 0).into()); + assert_eq!(chainpack_to_rpcvalue("8DE8A8BFFE").unwrap(), DateTime::from_epoch_msec_tz(1_514_764_800_000, 0).into()); + assert_eq!(chainpack_to_rpcvalue("8DE6DC0E02").unwrap(), DateTime::from_epoch_msec_tz(1_546_300_800_000, 0).into()); + assert_eq!(chainpack_to_rpcvalue("8DF00E60DC02").unwrap(), DateTime::from_epoch_msec_tz(1_577_836_800_000, 0).into()); + assert_eq!(chainpack_to_rpcvalue("8DF015EAF002").unwrap(), DateTime::from_epoch_msec_tz(1_609_459_200_000, 0).into()); + assert_eq!(chainpack_to_rpcvalue("8DF061258802").unwrap(), DateTime::from_epoch_msec_tz(1_924_992_000_000, 0).into()); + assert_eq!(chainpack_to_rpcvalue("8DF100AC656602").unwrap(), DateTime::from_epoch_msec_tz(2_240_611_200_000, 0).into()); + assert_eq!(chainpack_to_rpcvalue("8DF156D74D495F").unwrap(), DateTime::from_epoch_msec_tz(2_246_004_900_000, -36900).into()); + assert_eq!(chainpack_to_rpcvalue("8DF301533905E2375D").unwrap(), DateTime::from_epoch_msec_tz(2_246_004_900_123, -36900).into()); assert_eq!(chainpack_to_rpcvalue("8DF18169CEA7FE").unwrap(), DateTime::from_epoch_msec_tz(0, 0).into()); - assert_eq!(chainpack_to_rpcvalue("8DEDA8E7F2").unwrap(), DateTime::from_epoch_msec_tz(1493790723000, 0).into()); - assert_eq!(chainpack_to_rpcvalue("8DF1961334BEB4").unwrap(), DateTime::from_epoch_msec_tz(1493826723923, 0).into()); - assert_eq!(chainpack_to_rpcvalue("8DF28B0DE42CD95F").unwrap(), DateTime::from_epoch_msec_tz(1493790751123, 36000).into()); - assert_eq!(chainpack_to_rpcvalue("8DEDA6B572").unwrap(), DateTime::from_epoch_msec_tz(1493826723000, 0).into()); - assert_eq!(chainpack_to_rpcvalue("8DF182D3308815").unwrap(), DateTime::from_epoch_msec_tz(1493832123000, -5400).into()); - assert_eq!(chainpack_to_rpcvalue("8DF1961334BEB4").unwrap(), DateTime::from_epoch_msec_tz(1493826723923, 0).into()); - - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1517529600001, 0).into()), "8D04"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1517529600001, 3600).into()), "8D8211"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1543708800000, 0).into()), "8DE63DDA02"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1514764800000, 0).into()), "8DE8A8BFFE"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1546300800000, 0).into()), "8DE6DC0E02"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1577836800000, 0).into()), "8DF00E60DC02"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1609459200000, 0).into()), "8DF015EAF002"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1924992000000, 0).into()), "8DF061258802"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(2240611200000, 0).into()), "8DF100AC656602"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(2246004900000, -36900).into()), "8DF156D74D495F"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(2246004900123, -36900).into()), "8DF301533905E2375D"); + assert_eq!(chainpack_to_rpcvalue("8DEDA8E7F2").unwrap(), DateTime::from_epoch_msec_tz(1_493_790_723_000, 0).into()); + assert_eq!(chainpack_to_rpcvalue("8DF1961334BEB4").unwrap(), DateTime::from_epoch_msec_tz(1_493_826_723_923, 0).into()); + assert_eq!(chainpack_to_rpcvalue("8DF28B0DE42CD95F").unwrap(), DateTime::from_epoch_msec_tz(1_493_790_751_123, 36000).into()); + assert_eq!(chainpack_to_rpcvalue("8DEDA6B572").unwrap(), DateTime::from_epoch_msec_tz(1_493_826_723_000, 0).into()); + assert_eq!(chainpack_to_rpcvalue("8DF182D3308815").unwrap(), DateTime::from_epoch_msec_tz(1_493_832_123_000, -5400).into()); + assert_eq!(chainpack_to_rpcvalue("8DF1961334BEB4").unwrap(), DateTime::from_epoch_msec_tz(1_493_826_723_923, 0).into()); + + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_517_529_600_001, 0).into()), "8D04"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_517_529_600_001, 3600).into()), "8D8211"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_543_708_800_000, 0).into()), "8DE63DDA02"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_514_764_800_000, 0).into()), "8DE8A8BFFE"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_546_300_800_000, 0).into()), "8DE6DC0E02"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_577_836_800_000, 0).into()), "8DF00E60DC02"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_609_459_200_000, 0).into()), "8DF015EAF002"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_924_992_000_000, 0).into()), "8DF061258802"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(2_240_611_200_000, 0).into()), "8DF100AC656602"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(2_246_004_900_000, -36900).into()), "8DF156D74D495F"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(2_246_004_900_123, -36900).into()), "8DF301533905E2375D"); assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(0, 0).into()), "8DF18169CEA7FE"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1493790723000, 0).into()), "8DEDA8E7F2"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1493826723923, 0).into()), "8DF1961334BEB4"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1493790751123, 36000).into()), "8DF28B0DE42CD95F"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1493826723000, 0).into()), "8DEDA6B572"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1493832123000, -5400).into()), "8DF182D3308815"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1493826723923, 0).into()), "8DF1961334BEB4"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_493_790_723_000, 0).into()), "8DEDA8E7F2"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_493_826_723_923, 0).into()), "8DF1961334BEB4"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_493_790_751_123, 36000).into()), "8DF28B0DE42CD95F"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_493_826_723_000, 0).into()), "8DEDA6B572"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_493_832_123_000, -5400).into()), "8DF182D3308815"); + assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_493_826_723_923, 0).into()), "8DF1961334BEB4"); } #[test] diff --git a/src/cpon.rs b/src/cpon.rs index 7d78448..2a559a0 100644 --- a/src/cpon.rs +++ b/src/cpon.rs @@ -604,12 +604,12 @@ mod test test_cpon_round_trip("123.4", Decimal::new(1234, -1)); test_cpon_round_trip("0.123", Decimal::new(123, -3)); test_cpon_round_trip("-0.123", Decimal::new(-123, -3)); - test_cpon_round_trip("123000000e2", Decimal::new(123000000, 2)); + test_cpon_round_trip("123000000e2", Decimal::new(123_000_000, 2)); assert_eq!(Decimal::new(10000, 3).to_cpon_string(), "10000000."); assert_eq!(RpcValue::from_cpon("0e0").unwrap().as_decimal(), Decimal::new(0, 0)); assert_eq!(RpcValue::from_cpon("0.123e3").unwrap().as_decimal(), Decimal::new(123, 0)); - test_cpon_round_trip("1000000.", Decimal::new(1000000, 0)); - test_cpon_round_trip("50.03138741402532", Decimal::new(5003138741402532, -14)); + test_cpon_round_trip("1000000.", Decimal::new(1_000_000, 0)); + test_cpon_round_trip("50.03138741402532", Decimal::new(5_003_138_741_402_532, -14)); // We do not support such high precision. assert!(RpcValue::from_cpon("36.028797018963968").is_err()); assert_eq!(RpcValue::from_cpon(r#""foo""#).unwrap().as_str(), "foo"); @@ -696,21 +696,21 @@ mod test // read very long decimal without overflow error, value is capped assert_eq!(RpcValue::from_cpon("123456789012345678901234567890123456789012345678901234567890").unwrap().as_int(), i64::MAX); - assert_eq!(RpcValue::from_cpon("9223372036854775806").unwrap().as_int(), 9223372036854775806_i64); + assert_eq!(RpcValue::from_cpon("9223372036854775806").unwrap().as_int(), 9_223_372_036_854_775_806_i64); assert_eq!(RpcValue::from_cpon("9223372036854775807").unwrap().as_int(), i64::MAX); assert_eq!(RpcValue::from_cpon("9223372036854775808").unwrap().as_int(), i64::MAX); - assert_eq!(RpcValue::from_cpon("0x7FFFFFFFFFFFFFFE").unwrap().as_int(), 0x7FFFFFFFFFFFFFFE_i64); + assert_eq!(RpcValue::from_cpon("0x7FFFFFFFFFFFFFFE").unwrap().as_int(), 0x7FFF_FFFF_FFFF_FFFE_i64); assert_eq!(RpcValue::from_cpon("0x7FFFFFFFFFFFFFFF").unwrap().as_int(), i64::MAX); assert_eq!(RpcValue::from_cpon("0x8000000000000000").unwrap().as_int(), i64::MAX); assert_eq!(RpcValue::from_cpon("-123456789012345678901234567890123456789012345678901234567890").unwrap().as_int(), i64::MIN); - assert_eq!(RpcValue::from_cpon("-9223372036854775807").unwrap().as_int(), -9223372036854775807_i64); + assert_eq!(RpcValue::from_cpon("-9223372036854775807").unwrap().as_int(), -9_223_372_036_854_775_807_i64); assert_eq!(RpcValue::from_cpon("-9223372036854775808").unwrap().as_int(), i64::MIN); assert_eq!(RpcValue::from_cpon("-9223372036854775809").unwrap().as_int(), i64::MIN); - assert_eq!(RpcValue::from_cpon("-0x7FFFFFFFFFFFFFFF").unwrap().as_int(), -0x7FFFFFFFFFFFFFFF_i64); + assert_eq!(RpcValue::from_cpon("-0x7FFFFFFFFFFFFFFF").unwrap().as_int(), -0x7FFF_FFFF_FFFF_FFFF_i64); assert_eq!(RpcValue::from_cpon("-0x8000000000000000").unwrap().as_int(), i64::MIN); assert_eq!(RpcValue::from_cpon("-0x8000000000000001").unwrap().as_int(), i64::MIN); diff --git a/src/json.rs b/src/json.rs index ab461f6..6e2618f 100644 --- a/src/json.rs +++ b/src/json.rs @@ -549,21 +549,21 @@ mod test // read very long decimal without overflow error, value is capped assert_eq!(RpcValue::from_json("123456789012345678901234567890123456789012345678901234567890").unwrap().as_int(), i64::MAX); - assert_eq!(RpcValue::from_json("9223372036854775806").unwrap().as_int(), 9223372036854775806_i64); + assert_eq!(RpcValue::from_json("9223372036854775806").unwrap().as_int(), 9_223_372_036_854_775_806_i64); assert_eq!(RpcValue::from_json("9223372036854775807").unwrap().as_int(), i64::MAX); assert_eq!(RpcValue::from_json("9223372036854775808").unwrap().as_int(), i64::MAX); - assert_eq!(RpcValue::from_json("0x7FFFFFFFFFFFFFFE").unwrap().as_int(), 0x7FFFFFFFFFFFFFFE_i64); + assert_eq!(RpcValue::from_json("0x7FFFFFFFFFFFFFFE").unwrap().as_int(), 0x7FFF_FFFF_FFFF_FFFE_i64); assert_eq!(RpcValue::from_json("0x7FFFFFFFFFFFFFFF").unwrap().as_int(), i64::MAX); assert_eq!(RpcValue::from_json("0x8000000000000000").unwrap().as_int(), i64::MAX); assert_eq!(RpcValue::from_json("-123456789012345678901234567890123456789012345678901234567890").unwrap().as_int(), i64::MIN); - assert_eq!(RpcValue::from_json("-9223372036854775807").unwrap().as_int(), -9223372036854775807_i64); + assert_eq!(RpcValue::from_json("-9223372036854775807").unwrap().as_int(), -9_223_372_036_854_775_807_i64); assert_eq!(RpcValue::from_json("-9223372036854775808").unwrap().as_int(), i64::MIN); assert_eq!(RpcValue::from_json("-9223372036854775809").unwrap().as_int(), i64::MIN); - assert_eq!(RpcValue::from_json("-0x7FFFFFFFFFFFFFFF").unwrap().as_int(), -0x7FFFFFFFFFFFFFFF_i64); + assert_eq!(RpcValue::from_json("-0x7FFFFFFFFFFFFFFF").unwrap().as_int(), -0x7FFF_FFFF_FFFF_FFFF_i64); assert_eq!(RpcValue::from_json("-0x8000000000000000").unwrap().as_int(), i64::MIN); assert_eq!(RpcValue::from_json("-0x8000000000000001").unwrap().as_int(), i64::MIN); diff --git a/src/textrdwr.rs b/src/textrdwr.rs index dee0c8c..38bdbcb 100644 --- a/src/textrdwr.rs +++ b/src/textrdwr.rs @@ -234,7 +234,7 @@ pub trait TextReader : Reader { let ReadInt { value, digit_cnt, is_overflow, .. } = self.read_int(mantissa, true)?; decimal_overflow = decimal_overflow || is_overflow; mantissa = value; - if mantissa >= 36028797018963968 { + if mantissa >= 36_028_797_018_963_968 { decimal_overflow = true; } dec_cnt = digit_cnt as i64; From 14237fb1d994a646d49a48938cfbcf13db17e8c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 14:11:48 +0100 Subject: [PATCH 05/37] Fix possible truncation --- src/decimal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/decimal.rs b/src/decimal.rs index b35672f..5d00d7e 100644 --- a/src/decimal.rs +++ b/src/decimal.rs @@ -26,7 +26,7 @@ impl Decimal { } pub fn decode(&self) -> (i64, i8) { let m = self.0 >> 8; - let e = self.0 as i8; + let e = (self.0 & 0xFF) as i8; (m, e) } pub fn mantissa(&self) -> i64 { From a91e108fedfb1a2f95ca68e2b5e224cb71604b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 14:16:39 +0100 Subject: [PATCH 06/37] Use arguments directly in format string --- src/bin/cp2cp.rs | 12 ++++++------ src/chainpack.rs | 15 +++++++-------- src/cpon.rs | 6 +++--- src/datetime.rs | 18 +++++++++--------- src/json.rs | 12 ++++++------ src/metamap.rs | 4 ++-- src/textrdwr.rs | 6 +++--- src/util.rs | 8 ++++---- 8 files changed, 40 insertions(+), 41 deletions(-) diff --git a/src/bin/cp2cp.rs b/src/bin/cp2cp.rs index a386d62..27c78e5 100644 --- a/src/bin/cp2cp.rs +++ b/src/bin/cp2cp.rs @@ -53,7 +53,7 @@ struct ChainPackRpcBlockResult { } fn print_option(n: Option) { if let Some(n) = n { - println!("{}", n); + println!("{n}"); } else { println!(); } @@ -64,7 +64,7 @@ fn exit_with_result_and_code(result: &ChainPackRpcBlockResult, error: Option CODE_NOT_ENOUGH_DATA, ReadErrorReason::NotEnoughPrecision => CODE_READ_ERROR, ReadErrorReason::InvalidCharacter => { - eprintln!("Parse input error: {:?}", error); + eprintln!("Parse input error: {error:?}"); CODE_READ_ERROR } } @@ -133,7 +133,7 @@ fn main() { logger = logger.with_module_level(&module_name, LevelFilter::Trace); } } - logger.init().unwrap(); + logger.init().expect("Logger must work"); if opts.chainpack_rpc_block { opts.indent = None; @@ -143,7 +143,7 @@ fn main() { let mut reader: Box = match opts.file { None => Box::new(BufReader::new(io::stdin())), - Some(filename) => Box::new(BufReader::new(fs::File::open(filename).unwrap())), + Some(filename) => Box::new(BufReader::new(fs::File::open(filename).expect("Opening files must work"))), }; let read_result = if opts.cpon_input { @@ -158,7 +158,7 @@ fn main() { let input_value = match read_result { Err(e) => { - eprintln!("Parse input error: {:?}", e); + eprintln!("Parse input error: {e:?}"); process::exit(CODE_READ_ERROR); } Ok(rv) => rv, @@ -210,7 +210,7 @@ fn main() { }; if let Err(e) = res { - eprintln!("Write output error: {:?}", e); + eprintln!("Write output error: {e:?}"); process::exit(CODE_WRITE_ERROR); }; } diff --git a/src/chainpack.rs b/src/chainpack.rs index 81dd973..6d4381c 100644 --- a/src/chainpack.rs +++ b/src/chainpack.rs @@ -117,8 +117,7 @@ where let byte_cnt = Self::bytes_needed(bit_len); assert!( byte_cnt <= BYTE_CNT_MAX, - "Max int byte size {} exceeded", - BYTE_CNT_MAX + "Max int byte size {BYTE_CNT_MAX} exceeded" ); let mut bytes: [u8; BYTE_CNT_MAX as usize] = [0; BYTE_CNT_MAX as usize]; let mut num = number; @@ -342,7 +341,7 @@ where } fn make_error(&self, msg: &str, reason: ReadErrorReason) -> ReadError { self.byte_reader - .make_error(&format!("ChainPack read error - {}", msg), reason) + .make_error(&format!("ChainPack read error - {msg}"), reason) } /// return (n, bitlen) @@ -412,7 +411,7 @@ where match s { Ok(s) => Ok(Value::from(s)), Err(e) => Err(self.make_error( - &format!("Invalid string, Utf8 error: {}", e), + &format!("Invalid string, Utf8 error: {e}"), ReadErrorReason::InvalidCharacter, )), } @@ -428,7 +427,7 @@ where match s { Ok(s) => Ok(Value::from(s)), Err(e) => Err(self.make_error( - &format!("Invalid string, Utf8 error: {}", e), + &format!("Invalid string, Utf8 error: {e}"), ReadErrorReason::InvalidCharacter, )), } @@ -468,7 +467,7 @@ where k.as_str() } else { return Err(self.make_error( - &format!("Invalid Map key '{}'", k), + &format!("Invalid Map key '{k}'"), ReadErrorReason::InvalidCharacter, )); }; @@ -490,7 +489,7 @@ where k.as_i32() } else { return Err(self.make_error( - &format!("Invalid IMap key '{}'", k), + &format!("Invalid IMap key '{k}'"), ReadErrorReason::InvalidCharacter, )); }; @@ -615,7 +614,7 @@ where Value::from(()) } else { return Err(self.make_error( - &format!("Invalid Packing schema: {}", b), + &format!("Invalid Packing schema: {b}"), ReadErrorReason::InvalidCharacter, )); }; diff --git a/src/cpon.rs b/src/cpon.rs index 2a559a0..544a62f 100644 --- a/src/cpon.rs +++ b/src/cpon.rs @@ -363,7 +363,7 @@ impl<'a, R> CponReader<'a, R> b'A' ..= b'F' => Ok(b - b'A' + 10), b'a' ..= b'f' => Ok(b - b'a' + 10), b'0' ..= b'9' => Ok(b - b'0'), - c => Err(self.make_error(&format!("Illegal hex encoding character: {}", c), ReadErrorReason::InvalidCharacter)), + c => Err(self.make_error(&format!("Illegal hex encoding character: {c}"), ReadErrorReason::InvalidCharacter)), } } fn read_blob_esc(&mut self) -> Result { @@ -483,7 +483,7 @@ where R: Read self.byte_reader.get_byte() } fn make_error(&self, msg: &str, reason: ReadErrorReason) -> ReadError { - self.byte_reader.make_error(&format!("Cpon read error - {}", msg), reason) + self.byte_reader.make_error(&format!("Cpon read error - {msg}"), reason) } fn read_string(&mut self) -> Result { let mut buff: Vec = Vec::new(); @@ -515,7 +515,7 @@ where R: Read let s = std::str::from_utf8(&buff); match s { Ok(s) => Ok(Value::from(s)), - Err(e) => Err(self.make_error(&format!("Invalid String, Utf8 error: {}", e), ReadErrorReason::InvalidCharacter)), + Err(e) => Err(self.make_error(&format!("Invalid String, Utf8 error: {e}"), ReadErrorReason::InvalidCharacter)), } } } diff --git a/src/datetime.rs b/src/datetime.rs index 79da486..3ade215 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -88,7 +88,7 @@ impl DateTime { rest = &rest[3..]; } Err(err) => { - return Err(format!("Parsing DateTime msec part error: {}, in '{}", err, iso_str)) + return Err(format!("Parsing DateTime msec part error: {err}, in '{iso_str}")) } } } @@ -99,16 +99,16 @@ impl DateTime { if let Ok(hrs) = rest.parse::() { offset = 60 * 60 * hrs; } else { - return Err(format!("Invalid DateTime TZ(3) part: '{}, date time: {}", rest, iso_str)) + return Err(format!("Invalid DateTime TZ(3) part: '{rest}, date time: {iso_str}")) } } else if rest.len() == 5 { if let Ok(hrs) = rest.parse::() { offset = 60 * (60 * (hrs / 100) + (hrs % 100)); } else { - return Err(format!("Invalid DateTime TZ(5) part: '{}, date time: {}", rest, iso_str)) + return Err(format!("Invalid DateTime TZ(5) part: '{rest}, date time: {iso_str}")) } } else { - return Err(format!("Invalid DateTime TZ part: '{}, date time: {}", rest, iso_str)) + return Err(format!("Invalid DateTime TZ part: '{rest}, date time: {iso_str}")) } } let epoch_msec = (ndt.and_utc().timestamp() - (offset as i64)) * 1000 + (msec as i64); @@ -116,7 +116,7 @@ impl DateTime { return Ok(dt) } } - Err(format!("Invalid DateTime: '{:?}", iso_str)) + Err(format!("Invalid DateTime: '{iso_str:?}")) } pub fn epoc_msec_utc_offset(&self) -> (i64, i32) { let msec= self.0 / (TZ_MASK + 1); @@ -151,10 +151,10 @@ impl DateTime { let ms = self.epoch_msec() % 1000; match opts.include_millis { IncludeMilliseconds::Never => {} - IncludeMilliseconds::Always => { s.push_str(&format!(".{:03}", ms)); } + IncludeMilliseconds::Always => { s.push_str(&format!(".{ms:03}")); } IncludeMilliseconds::WhenNonZero => { if ms > 0 { - s.push_str(&format!(".{:03}", ms)); + s.push_str(&format!(".{ms:03}")); } } } @@ -172,9 +172,9 @@ impl DateTime { } let offset_hr = offset / 60 / 60; let offset_min = offset / 60 % 60; - s += &format!("{:02}", offset_hr); + s += &format!("{offset_hr:02}"); if offset_min > 0 { - s += &format!("{:02}", offset_min); + s += &format!("{offset_min:02}"); } } } diff --git a/src/json.rs b/src/json.rs index 6e2618f..5a52aad 100644 --- a/src/json.rs +++ b/src/json.rs @@ -251,7 +251,7 @@ impl<'a, R> JsonReader<'a, R> let val = match t { Some("Blob") => { if let Value::String(hex) = v { - let data = hex::decode(hex.as_str()).map_err(|e| self.make_error(&format!("Hex blob decode error: {}.", e), ReadErrorReason::InvalidCharacter))?; + let data = hex::decode(hex.as_str()).map_err(|e| self.make_error(&format!("Hex blob decode error: {e}."), ReadErrorReason::InvalidCharacter))?; Value::from(data) } else { return Err(self.make_error("Blob must be encoded as hex string.", ReadErrorReason::InvalidCharacter)) @@ -259,7 +259,7 @@ impl<'a, R> JsonReader<'a, R> } Some("DateTime") => { if let Value::String(dt) = v { - let dt = DateTime::from_iso_str(dt).map_err(|e| self.make_error(&format!("DateTime decode error: {}.", e), ReadErrorReason::InvalidCharacter))?; + let dt = DateTime::from_iso_str(dt).map_err(|e| self.make_error(&format!("DateTime decode error: {e}."), ReadErrorReason::InvalidCharacter))?; Value::from(dt) } else { return Err(self.make_error("DateTime must be encoded as ISO string.", ReadErrorReason::InvalidCharacter)) @@ -269,7 +269,7 @@ impl<'a, R> JsonReader<'a, R> if let Value::Map(im) = v { let mut imap = crate::IMap::default(); for (k, v) in im.iter() { - let ik = k.parse::().map_err(|e| self.make_error(&format!("IMap key decode error: {}.", e), ReadErrorReason::InvalidCharacter))?; + let ik = k.parse::().map_err(|e| self.make_error(&format!("IMap key decode error: {e}."), ReadErrorReason::InvalidCharacter))?; imap.insert(ik, v.clone()); } Value::from(imap) @@ -294,7 +294,7 @@ where R: Read self.byte_reader.get_byte() } fn make_error(&self, msg: &str, reason: ReadErrorReason) -> ReadError { - self.byte_reader.make_error(&format!("Cpon read error - {}", msg), reason) + self.byte_reader.make_error(&format!("Cpon read error - {msg}"), reason) } fn read_string(&mut self) -> Result { let mut buff: Vec = Vec::new(); @@ -321,7 +321,7 @@ where R: Read hex.push(self.get_byte()? as char); hex.push(self.get_byte()? as char); hex.push(self.get_byte()? as char); - let code_point = u32::from_str_radix(&hex, 16).map_err(|e| self.make_error(&format!("Invalid unicode escape sequence: {:?} - {}", hex, e), ReadErrorReason::InvalidCharacter))?; + let code_point = u32::from_str_radix(&hex, 16).map_err(|e| self.make_error(&format!("Invalid unicode escape sequence: {hex:?} - {e}"), ReadErrorReason::InvalidCharacter))?; let ch = char::from_u32(code_point).ok_or(self.make_error(&format!("Invalid code point: {code_point}"), ReadErrorReason::InvalidCharacter))?; let mut utf8 = [0; 4]; let s = ch.encode_utf8(&mut utf8); @@ -346,7 +346,7 @@ where R: Read let s = std::str::from_utf8(&buff); match s { Ok(s) => Ok(Value::from(s)), - Err(e) => Err(self.make_error(&format!("Invalid String, Utf8 error: {}", e), ReadErrorReason::InvalidCharacter)), + Err(e) => Err(self.make_error(&format!("Invalid String, Utf8 error: {e}"), ReadErrorReason::InvalidCharacter)), } } } diff --git a/src/metamap.rs b/src/metamap.rs index 14fda8b..5f476e7 100644 --- a/src/metamap.rs +++ b/src/metamap.rs @@ -133,11 +133,11 @@ impl fmt::Display for MetaMap { let mut wr = CponWriter::new(&mut buff); let res = wr.write_meta(self); if let Err(e) = res { - log::warn!("to_cpon write with error: {}", e); + log::warn!("to_cpon write with error: {e}"); return write!(fmt, "") } match String::from_utf8(buff) { - Ok(s) => write!(fmt, "{}", s), + Ok(s) => write!(fmt, "{s}"), Err(_) => write!(fmt, ""), } } diff --git a/src/textrdwr.rs b/src/textrdwr.rs index 38bdbcb..6267e9e 100644 --- a/src/textrdwr.rs +++ b/src/textrdwr.rs @@ -12,7 +12,7 @@ pub trait TextWriter : Writer { Ok(self.write_count() - cnt) } fn write_double(&mut self, n: f64) -> WriteResult { - let s = format!("{}", n); + let s = n.to_string(); let cnt = self.write_bytes(s.as_bytes())?; Ok(self.write_count() - cnt) } @@ -94,7 +94,7 @@ pub trait TextReader : Reader { for c in token.as_bytes() { let b = self.get_byte()?; if b != *c { - return Err(self.make_error(&format!("Expected '{}'.", token), ReadErrorReason::InvalidCharacter)) + return Err(self.make_error(&format!("Expected '{token}'."), ReadErrorReason::InvalidCharacter)) } } Ok(()) @@ -312,7 +312,7 @@ pub trait TextReader : Reader { _ => return Err(self.make_error("Read MetaMap key internal error", ReadErrorReason::InvalidCharacter)), } }, - _ => return Err(self.make_error(&format!("Invalid Map key '{}'", b), ReadErrorReason::InvalidCharacter)), + _ => return Err(self.make_error(&format!("Invalid Map key '{b}'"), ReadErrorReason::InvalidCharacter)), }; self.skip_white_insignificant()?; let val = self.read()?; diff --git a/src/util.rs b/src/util.rs index 20b362d..b3707f1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -34,7 +34,7 @@ pub fn hex_array(data: &[u8]) -> String { if ret.len() > 1 { ret += ","; } - ret += &format!("0x{:02x}", b); + ret += &format!("0x{b:02x}"); } ret += "]"; ret @@ -53,21 +53,21 @@ pub fn hex_dump(data: &[u8]) -> String { if i > 0 { ret += "\n"; } - ret += &format!("{:04x} ", i); + ret += &format!("{i:04x} "); } hex_line.clear(); char_line.clear(); } let hex_str = match byte { None => { " ".to_string() } - Some(b) => { format!("{:02x} ", b) } + Some(b) => { format!("{b:02x} ") } }; let c_str = match byte { None => { " ".to_string() } Some(b) => { let c = b as char; let c = if c >= ' ' && c < (127 as char) { c } else { '.' }; - format!("{}", c) + c.to_string() } }; hex_line += &hex_str; From 54b1b004b4dc9d7168fab59e04c9f276e7d14dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 14:31:41 +0100 Subject: [PATCH 07/37] Fix casting issues --- src/bin/cp2cp.rs | 1 + src/chainpack.rs | 36 +++++++++++++++++++----------------- src/cpon.rs | 3 ++- src/datetime.rs | 5 +++-- src/decimal.rs | 3 ++- src/jaq.rs | 5 ++++- src/json.rs | 4 ++-- src/metamap.rs | 2 ++ src/rpcvalue.rs | 21 +++++++++++++++------ src/serde/de.rs | 4 +++- src/serde/ser.rs | 28 ++++++++++++---------------- src/textrdwr.rs | 7 ++++--- 12 files changed, 69 insertions(+), 50 deletions(-) diff --git a/src/bin/cp2cp.rs b/src/bin/cp2cp.rs index 27c78e5..2d827dd 100644 --- a/src/bin/cp2cp.rs +++ b/src/bin/cp2cp.rs @@ -85,6 +85,7 @@ fn process_chainpack_rpc_block(mut reader: Box) -> ! { cpon: "".to_string(), }; let mut rd = ChainPackReader::new(&mut reader); + #[expect(clippy::cast_possible_truncation, reason = "We assume pointer size is 64-bit")] match rd.read_uint_data() { Ok(frame_length) => { result.block_length = Some(frame_length as usize + rd.position()); diff --git a/src/chainpack.rs b/src/chainpack.rs index 6d4381c..168d6ab 100644 --- a/src/chainpack.rs +++ b/src/chainpack.rs @@ -1,3 +1,4 @@ +#![allow(clippy::cast_possible_truncation, reason = "Lots of casting here")] use crate::reader::{ByteReader, ReadError, ReadErrorReason, Reader}; use crate::rpcvalue::{IMap, Map}; use crate::writer::{ByteWriter, Writer}; @@ -78,7 +79,7 @@ where } const SIG_TABLE_4BIT: [u8; 16] = [0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4]; len += SIG_TABLE_4BIT[n as usize]; - len as u32 + u32::from(len) } /// number of bytes needed to encode bit_len fn bytes_needed(bit_len: u32) -> u32 { @@ -150,10 +151,10 @@ where let mut num: u64; let neg; if number < 0 { - num = (-number) as u64; + num = (-number).cast_unsigned(); neg = true; } else { - num = number as u64; + num = number.cast_unsigned(); neg = false; }; @@ -170,6 +171,7 @@ where fn write_int(&mut self, n: i64) -> WriteResult { let cnt = self.byte_writer.count(); if (0..64).contains(&n) { + #[expect(clippy::cast_sign_loss, reason = "n is already between 0 and 64")] self.write_byte(((n % 64) + 64) as u8)?; } else { self.write_byte(PackingSchema::Int as u8)?; @@ -196,7 +198,7 @@ where fn write_decimal(&mut self, decimal: &Decimal) -> WriteResult { let cnt = self.write_byte(PackingSchema::Decimal as u8)?; self.write_int_data(decimal.mantissa())?; - self.write_int_data(decimal.exponent() as i64)?; + self.write_int_data(i64::from(decimal.exponent()))?; Ok(self.byte_writer.count() - cnt) } fn write_datetime(&mut self, dt: &DateTime) -> WriteResult { @@ -209,7 +211,7 @@ where } if offset != 0 { msecs <<= 7; - msecs |= offset as i64; + msecs |= i64::from(offset); } msecs <<= 2; if offset != 0 { @@ -241,7 +243,7 @@ where fn write_imap(&mut self, map: &IMap) -> WriteResult { let cnt = self.write_byte(PackingSchema::IMap as u8)?; for (k, v) in map { - self.write_int(*k as i64)?; + self.write_int(i64::from(*k))?; self.write(v)?; } self.write_byte(PackingSchema::TERM as u8)?; @@ -272,7 +274,7 @@ where for k in map.0.iter() { match &k.key { MetaKey::Str(s) => self.write_string(s)?, - MetaKey::Int(i) => self.write_int(*i as i64)?, + MetaKey::Int(i) => self.write_int(i64::from(*i))?, }; self.write(&k.value)?; } @@ -353,19 +355,19 @@ where let bitlen; if (head & 128) == 0 { bytes_to_read_cnt = 0; - num = (head & 127) as u64; + num = u64::from(head & 127); bitlen = 7; } else if (head & 64) == 0 { bytes_to_read_cnt = 1; - num = (head & 63) as u64; + num = u64::from(head & 63); bitlen = 6 + 8; } else if (head & 32) == 0 { bytes_to_read_cnt = 2; - num = (head & 31) as u64; + num = u64::from(head & 31); bitlen = 5 + 2 * 8; } else if (head & 16) == 0 { bytes_to_read_cnt = 3; - num = (head & 15) as u64; + num = u64::from(head & 15); bitlen = 4 + 3 * 8; } else if head == 0xFF { return Err(self.make_error( @@ -378,7 +380,7 @@ where } for _ in 0..bytes_to_read_cnt { let r = self.get_byte()?; - num = (num << 8) + (r as u64); + num = (num << 8) + u64::from(r); } Ok((num, bitlen)) } @@ -390,9 +392,9 @@ where let (num, bitlen) = self.read_uint_data_helper()?; let sign_bit_mask = (1_u64) << (bitlen - 1); let neg = (num & sign_bit_mask) != 0; - let mut snum = num as i64; + let mut snum = num.cast_signed(); if neg { - snum &= !(sign_bit_mask as i64); + snum &= !(sign_bit_mask.cast_signed()); snum = -snum; } Ok(snum) @@ -514,7 +516,7 @@ where d *= 1000; } d += SHV_EPOCH_MSEC; - let dt = DateTime::from_epoch_msec_tz(d, (offset as i32 * 15) * 60); + let dt = DateTime::from_epoch_msec_tz(d, (i32::from(offset) * 15) * 60); Ok(Value::from(dt)) } fn read_double_data(&mut self) -> Result { @@ -577,10 +579,10 @@ where let v = if b < 128 { if (b & 64) == 0 { // tiny UInt - Value::from((b & 63) as u64) + Value::from(u64::from(b & 63)) } else { // tiny Int - Value::from((b & 63) as i64) + Value::from(i64::from(b & 63)) } } else if b == PackingSchema::Int as u8 { let n = self.read_int_data()?; diff --git a/src/cpon.rs b/src/cpon.rs index 544a62f..a4ef035 100644 --- a/src/cpon.rs +++ b/src/cpon.rs @@ -258,7 +258,7 @@ impl<'a, W> CponWriter<'a, W> self.write_byte(b',')?; } self.indent_element(is_oneliner, n == 0)?; - self.write_int(*k as i64)?; + self.write_int(i64::from(*k))?; self.write_byte(b':')?; self.write(v)?; } @@ -440,6 +440,7 @@ impl<'a, R> CponReader<'a, R> let key = if is_negative { -value } else { value }; self.skip_white_insignificant()?; let val = self.read()?; + #[expect(clippy::cast_possible_truncation, reason = "We hope that the key is small enough to fit")] map.insert(key as i32, val); } Ok(Value::from(map)) diff --git a/src/datetime.rs b/src/datetime.rs index 3ade215..12e699d 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -63,7 +63,7 @@ impl DateTime { let mut msec = epoch_msec; // offset in quarters of hour msec *= TZ_MASK + 1; - let offset = (utc_offset_sec / 60 / 15) as i64; + let offset = i64::from(utc_offset_sec / 60 / 15); msec |= offset & TZ_MASK; DateTime(msec) } @@ -111,7 +111,7 @@ impl DateTime { return Err(format!("Invalid DateTime TZ part: '{rest}, date time: {iso_str}")) } } - let epoch_msec = (ndt.and_utc().timestamp() - (offset as i64)) * 1000 + (msec as i64); + let epoch_msec = (ndt.and_utc().timestamp() - i64::from(offset)) * 1000 + i64::from(msec); let dt = DateTime::from_epoch_msec_tz(epoch_msec, offset); return Ok(dt) } @@ -125,6 +125,7 @@ impl DateTime { // sign extension offset |= !TZ_MASK; } + #[expect(clippy::cast_possible_truncation, reason = "We hope that the offset is small enough to fit")] let offset = (offset * 15 * 60) as i32; (msec, offset) } diff --git a/src/decimal.rs b/src/decimal.rs index 5d00d7e..1bb701b 100644 --- a/src/decimal.rs +++ b/src/decimal.rs @@ -1,3 +1,4 @@ +#![allow(clippy::cast_sign_loss, clippy::cast_possible_truncation, clippy::cast_precision_loss, reason = "Lots of casting here")] /// mantisa: 56, exponent: 8; /// I'm storing whole Decimal in one i64 to keep size_of RpcValue == 24 #[derive(Debug, Copy, Clone)] @@ -7,7 +8,7 @@ impl Decimal { pub fn new(mantissa: i64, exponent: i8) -> Decimal { let mut n = mantissa << 8; - n |= (exponent as i64) & 0xff; + n |= i64::from(exponent) & 0xff; Decimal(n) } pub fn normalize(&self) -> Decimal { diff --git a/src/jaq.rs b/src/jaq.rs index f5b4b22..3da3e3f 100644 --- a/src/jaq.rs +++ b/src/jaq.rs @@ -150,6 +150,7 @@ impl jaq_all::jaq_std::ValT for RpcValue { fn as_isize(&self) -> Option { match self.value { + #[expect(clippy::cast_possible_truncation, reason = "We assume pointer size is 64-bit")] Value::Int(num) => Some(num as isize), _ => None, } @@ -191,7 +192,7 @@ impl jaq_all::jaq_core::ValT for RpcValue { Box::new(list .into_iter() .enumerate() - .map(|(idx, val)| Ok((RpcValue::from(idx as isize), val)))) + .map(|(idx, val)| Ok((RpcValue::from(idx.cast_signed()), val)))) } Value::Map(map) => { Box::new(map @@ -231,7 +232,9 @@ impl jaq_all::jaq_core::ValT for RpcValue { fn index(self, index: &Self) -> ValR { match (&self.value, &index.value) { (Value::Null, _) => Ok(RpcValue::null()), + #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss, reason = "We assume pointer size is 64-bit")] (Value::String(rv), Value::Int(i)) => Ok(rv.chars().nth(*i as usize).map(|cha| cha.to_string()).into()), + #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss, reason = "We assume pointer size is 64-bit")] (Value::List(list), Value::Int(i)) => Ok((*list).get(*i as usize).cloned().unwrap_or(RpcValue::null())), (Value::Map(o), Value::String(key)) => Ok(o.get(key.as_str()).cloned().unwrap_or(RpcValue::null())), (_s, _) => Err(Error::typ(self, "")), diff --git a/src/json.rs b/src/json.rs index 5a52aad..73843d5 100644 --- a/src/json.rs +++ b/src/json.rs @@ -111,7 +111,7 @@ impl<'a, W> JsonWriter<'a, W> self.write_byte(b',')?; } self.write_byte(b'"')?; - self.write_int(*k as i64)?; + self.write_int(i64::from(*k))?; self.write_byte(b'"')?; self.write_byte(b':')?; self.write(v)?; @@ -209,7 +209,7 @@ where W: Write self.write_bytes("false".as_bytes()) }, Value::Int(n) => self.write_int(*n), - Value::UInt(n) => self.write_int(*n as i64), + Value::UInt(n) => self.write_int(n.cast_signed()), Value::String(s) => self.write_string(s), Value::Blob(b) => self.write_blob(b), Value::Double(n) => self.write_double(*n), diff --git a/src/metamap.rs b/src/metamap.rs index 5f476e7..69caf6d 100644 --- a/src/metamap.rs +++ b/src/metamap.rs @@ -42,11 +42,13 @@ impl GetIndex for i32 { } impl GetIndex for u32 { fn make_key(&self) -> GetKey<'_> { + #[expect(clippy::cast_possible_wrap, reason = "We hope that the key is small enough to fit")] GetKey::Int(*self as i32) } } impl GetIndex for usize { fn make_key(&self) -> GetKey<'_> { + #[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap, reason = "We hope that the key is small enough to fit")] GetKey::Int(*self as i32) } } diff --git a/src/rpcvalue.rs b/src/rpcvalue.rs index 9f1c05f..aa44465 100644 --- a/src/rpcvalue.rs +++ b/src/rpcvalue.rs @@ -934,11 +934,13 @@ impl GetIndex for i32 { } impl GetIndex for u32 { fn make_key(&self) -> GetKey<'_> { + #[expect(clippy::cast_possible_wrap, reason = "We hope that the key is small enough to fit")] GetKey::Int(*self as i32) } } impl GetIndex for usize { fn make_key(&self) -> GetKey<'_> { + #[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap, reason = "We hope that the key is small enough to fit")] GetKey::Int(*self as i32) } } @@ -994,22 +996,26 @@ impl RpcValue { pub fn as_i64(&self) -> i64 { match &self.value { Value::Int(d) => *d, - Value::UInt(d) => *d as i64, + Value::UInt(d) => d.cast_signed(), _ => 0, } } pub fn as_i32(&self) -> i32 { - self.as_i64() as i32 + #[expect(clippy::cast_possible_truncation, reason = "If the user wants an i32, we don't care about overflows")] + let res = self.as_i64() as i32; + res } pub fn as_u64(&self) -> u64 { match &self.value { - Value::Int(d) => *d as u64, + Value::Int(d) => d.cast_unsigned(), Value::UInt(d) => *d, _ => 0, } } pub fn as_u32(&self) -> u32 { - self.as_u64() as u32 + #[expect(clippy::cast_possible_truncation, reason = "If the user wants an u32, we don't care about overflows")] + let res = self.as_u64() as u32; + res } pub fn as_f64(&self) -> f64 { match &self.value { @@ -1019,7 +1025,9 @@ impl RpcValue { } pub fn as_usize(&self) -> usize { match &self.value { + #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss, reason = "We expect a 64-bit platform")] Value::Int(d) => *d as usize, + #[expect(clippy::cast_possible_truncation, reason = "We expect a 64-bit platform")] Value::UInt(d) => *d as usize, _ => 0_usize, } @@ -1084,6 +1092,7 @@ impl RpcValue { { match key.make_key() { GetKey::Int(ix) => match &self.value { + #[expect(clippy::cast_sign_loss, reason = "We expect the int to be positive")] Value::List(lst) => lst.get(ix as usize), Value::IMap(map) => map.get(&ix), _ => None, @@ -1242,8 +1251,8 @@ mod test { let dt = chrono::offset::Local::now(); let rv = RpcValue::from(dt); assert_eq!( - rv.as_datetime().epoch_msec() + rv.as_datetime().utc_offset() as i64 * 1000, - dt.timestamp_millis() + dt.offset().fix().local_minus_utc() as i64 * 1000 + rv.as_datetime().epoch_msec() + i64::from(rv.as_datetime().utc_offset()) * 1000, + dt.timestamp_millis() + i64::from(dt.offset().fix().local_minus_utc()) * 1000 ); let vec1 = vec![RpcValue::from(123), RpcValue::from("foo")]; diff --git a/src/serde/de.rs b/src/serde/de.rs index 30dbae6..efadc17 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -103,7 +103,7 @@ impl<'de, 'a> serde::Deserializer<'de> for ValueDeserializer<'a> { { match self.value { Value::UInt(u) => visitor.visit_u64(*u), - Value::Int(i) if *i >= 0 => visitor.visit_u64(*i as u64), + Value::Int(i) if *i >= 0 => visitor.visit_u64(i.cast_unsigned()), // Value::Double(f) if *f >= 0.0 => visitor.visit_u64(*f as u64), _ => Err(de::Error::custom("expected unsigned integer-compatible value")), } @@ -137,7 +137,9 @@ impl<'de, 'a> serde::Deserializer<'de> for ValueDeserializer<'a> { match self.value { Value::Double(f) => visitor.visit_f64(*f), Value::Decimal(decimal) => visitor.visit_f64(decimal.to_f64()), + #[expect(clippy::cast_precision_loss, reason = "We hope we don't lose precision by casting to f64")] Value::Int(i) => visitor.visit_f64(*i as f64), + #[expect(clippy::cast_precision_loss, reason = "We hope we don't lose precision by casting to f64")] Value::UInt(u) => visitor.visit_f64(*u as f64), _ => Err(de::Error::custom("expected float-compatible value")), } diff --git a/src/serde/ser.rs b/src/serde/ser.rs index c3830c5..098d76b 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -44,43 +44,43 @@ impl serde::Serializer for ValueSerializer { } fn serialize_i8(self, v: i8) -> Result { - Ok(Value::Int(v as _)) + Ok(Value::Int(<_>::from(v))) } fn serialize_i16(self, v: i16) -> Result { - Ok(Value::Int(v as _)) + Ok(Value::Int(<_>::from(v))) } fn serialize_i32(self, v: i32) -> Result { - Ok(Value::Int(v as _)) + Ok(Value::Int(<_>::from(v))) } fn serialize_i64(self, v: i64) -> Result { - Ok(Value::Int(v as _)) + Ok(Value::Int(v)) } fn serialize_u8(self, v: u8) -> Result { - Ok(Value::UInt(v as _)) + Ok(Value::UInt(<_>::from(v))) } fn serialize_u16(self, v: u16) -> Result { - Ok(Value::UInt(v as _)) + Ok(Value::UInt(<_>::from(v))) } fn serialize_u32(self, v: u32) -> Result { - Ok(Value::UInt(v as _)) + Ok(Value::UInt(<_>::from(v))) } fn serialize_u64(self, v: u64) -> Result { - Ok(Value::UInt(v as _)) + Ok(Value::UInt(v)) } fn serialize_f32(self, v: f32) -> Result { - Ok(Value::Double(v as _)) + Ok(Value::Double(<_>::from(v))) } fn serialize_f64(self, v: f64) -> Result { - Ok(Value::Double(v as _)) + Ok(Value::Double(v)) } fn serialize_char(self, v: char) -> Result { @@ -669,12 +669,8 @@ impl serde::ser::SerializeMap for ValueSerializeIMap { fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> { if let Value::Int(i) = key.serialize(ValueSerializer)? { - if i <= i32::MAX as _ { - self.next_key = Some(i as _); - Ok(()) - } else { - Err(Error::custom("IMap keys must be i32-compatible")) - } + self.next_key = Some(i32::try_from(i).map_err(|_err| Error::custom("IMap keys must be i32-compatible"))?); + Ok(()) } else { Err(Error::custom("IMap key must be an integer")) } diff --git a/src/textrdwr.rs b/src/textrdwr.rs index 6267e9e..4a5e5c8 100644 --- a/src/textrdwr.rs +++ b/src/textrdwr.rs @@ -120,7 +120,7 @@ pub trait TextReader : Reader { let mut is_overflow = false; fn add_digit(val: i64, base: i64, digit: u8) -> Option { let res = val.checked_mul(base)?; - let res = res.checked_add(digit as i64)?; + let res = res.checked_add(i64::from(digit))?; Some(res) } loop { @@ -237,7 +237,7 @@ pub trait TextReader : Reader { if mantissa >= 36_028_797_018_963_968 { decimal_overflow = true; } - dec_cnt = digit_cnt as i64; + dec_cnt = i64::from(digit_cnt); } b'e' | b'E' => { if state != State::Mantissa && state != State::Decimals { @@ -263,13 +263,14 @@ pub trait TextReader : Reader { if decimal_overflow { return Err(self.make_error("Not enough precision to read the Decimal", ReadErrorReason::InvalidCharacter)) } + #[expect(clippy::cast_possible_truncation, reason = "We hope that the new exponent is not big enough to truncate")] return Ok(Value::from(Decimal::new(mantissa, (exponent - dec_cnt) as i8))) } if is_uint { if decimal_overflow { return Ok(Value::from(i64::MAX as u64)) } - return Ok(Value::from(mantissa as u64)) + return Ok(Value::from(mantissa.cast_unsigned())) } if decimal_overflow { return Ok(Value::from(if is_negative { i64::MIN } else { i64::MAX })) From 5784bcf1962562540af9bd8573a2561d5ba1eb8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:20:24 +0100 Subject: [PATCH 08/37] Fix indexing issues --- libshvproto-macros/src/lib.rs | 4 ++-- src/chainpack.rs | 1 + src/datetime.rs | 6 +++--- src/metamap.rs | 4 ++-- src/util.rs | 1 + 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/libshvproto-macros/src/lib.rs b/libshvproto-macros/src/lib.rs index e5d99d8..15143fa 100644 --- a/libshvproto-macros/src/lib.rs +++ b/libshvproto-macros/src/lib.rs @@ -15,7 +15,7 @@ fn is_option(ty: &syn::Type) -> bool { if !(typepath.qself.is_none() && typepath.path.segments.len() == 1) { return false } - let segment = &typepath.path.segments[0]; + let segment = typepath.path.segments.first().expect("len() is 1"); if segment.ident != "Option" { return false; @@ -27,7 +27,7 @@ fn is_option(ty: &syn::Type) -> bool { return false; } - matches!(data.args[0], syn::GenericArgument::Type(_)) + matches!(data.args.first().expect("len() is 1"), syn::GenericArgument::Type(_)) } fn get_type(ty: &syn::Type) -> Option { diff --git a/src/chainpack.rs b/src/chainpack.rs index 168d6ab..95ded69 100644 --- a/src/chainpack.rs +++ b/src/chainpack.rs @@ -1,4 +1,5 @@ #![allow(clippy::cast_possible_truncation, reason = "Lots of casting here")] +#![allow(clippy::indexing_slicing, reason = "Lots of indexing here")] use crate::reader::{ByteReader, ReadError, ReadErrorReason, Reader}; use crate::rpcvalue::{IMap, Map}; use crate::writer::{ByteWriter, Writer}; diff --git a/src/datetime.rs b/src/datetime.rs index 12e699d..b43ca78 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -1,4 +1,4 @@ -//use crate::rpcvalue::RpcValue; +#![allow(clippy::string_slice, reason = "strings are not utf8")] use std::cmp::Ordering; use std::fmt; @@ -79,7 +79,7 @@ impl DateTime { let mut msec = 0; let mut offset = 0; let mut rest = &s[PATTERN.len()..]; - if !rest.is_empty() && rest.as_bytes()[0] == b'.' { + if let Some(b'.') = rest.as_bytes().first() { rest = &rest[1..]; if rest.len() >= 3 { match rest[..3].parse::() { @@ -94,7 +94,7 @@ impl DateTime { } } if !rest.is_empty() { - if rest.len() == 1 && rest.as_bytes()[0] == b'Z' { + if rest.len() == 1 && *rest.as_bytes().first().expect("len() is 1") == b'Z' { } else if rest.len() == 3 { if let Ok(hrs) = rest.parse::() { offset = 60 * 60 * hrs; diff --git a/src/metamap.rs b/src/metamap.rs index 69caf6d..37ce3d2 100644 --- a/src/metamap.rs +++ b/src/metamap.rs @@ -81,7 +81,7 @@ impl MetaMap { let key = MetaKey::from(&key); self.0.push(MetaKeyVal{key, value }) }, - Some(ix) => self.0[ix].value = value, + Some(ix) => self.0.get_mut(ix).expect("The value has been found with .find()").value = value, } self } @@ -97,7 +97,7 @@ impl MetaMap { where I: GetIndex { match self.find(&key) { - Some(ix) => Some(&self.0[ix].value), + Some(ix) => Some(&self.0.get(ix).expect("The value has been found with .find()").value), None => None, } } diff --git a/src/util.rs b/src/util.rs index b3707f1..61a5849 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,4 @@ +#![allow(clippy::indexing_slicing, reason = "Lots of indexing here")] use log::LevelFilter; pub fn parse_log_verbosity<'a>(verbosity: &'a str, module_path: &'a str) -> Vec<(Option<&'a str>, LevelFilter)> { From b24905883f96ccb093616c5fd0335d6f289a7848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:21:29 +0100 Subject: [PATCH 09/37] Remove unnecessary semicolons --- src/bin/cp2cp.rs | 4 ++-- src/chainpack.rs | 2 +- src/cpon.rs | 2 +- src/json.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bin/cp2cp.rs b/src/bin/cp2cp.rs index 2d827dd..0fdd96c 100644 --- a/src/bin/cp2cp.rs +++ b/src/bin/cp2cp.rs @@ -107,7 +107,7 @@ fn process_chainpack_rpc_block(mut reader: Box) -> ! { Err(e) => { exit_with_result_and_code(&result, Some(e.reason)); } - }; + } match rd.read() { Ok(rv) => { result.cpon = rv.to_cpon().to_string(); @@ -213,6 +213,6 @@ fn main() { if let Err(e) = res { eprintln!("Write output error: {e:?}"); process::exit(CODE_WRITE_ERROR); - }; + } } } diff --git a/src/chainpack.rs b/src/chainpack.rs index 95ded69..8ac2ea5 100644 --- a/src/chainpack.rs +++ b/src/chainpack.rs @@ -157,7 +157,7 @@ where } else { num = number.cast_unsigned(); neg = false; - }; + } let bitlen = Self::significant_bits_part_length(num) + 1; // add sign bit if neg { diff --git a/src/cpon.rs b/src/cpon.rs index a4ef035..389445f 100644 --- a/src/cpon.rs +++ b/src/cpon.rs @@ -69,7 +69,7 @@ impl<'a, W> CponWriter<'a, W> } }, None => break, - }; + } n += 1; }; true diff --git a/src/json.rs b/src/json.rs index 73843d5..db89b53 100644 --- a/src/json.rs +++ b/src/json.rs @@ -386,7 +386,7 @@ impl Reader for JsonReader<'_, R> break 'a RpcValue::new(val, None); } } - }; + } break 'a RpcValue::new(val, None); }; Ok(rv) From 7709e7e504f23a4e5ee2150e08a0aa93282ccc95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:22:17 +0100 Subject: [PATCH 10/37] Use String::default Instead of inferring with the variable type. --- src/util.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util.rs b/src/util.rs index 61a5849..e8bf354 100644 --- a/src/util.rs +++ b/src/util.rs @@ -41,9 +41,9 @@ pub fn hex_array(data: &[u8]) -> String { ret } pub fn hex_dump(data: &[u8]) -> String { - let mut ret: String = Default::default(); - let mut hex_line: String = Default::default(); - let mut char_line: String = Default::default(); + let mut ret = String::default(); + let mut hex_line = String::default(); + let mut char_line = String::default(); let box_size = (data.len() / 16 + 1) * 16 + 1; for i in 0..box_size { let byte = if i < data.len() { Some(data[i]) } else { None }; From 89126740ad816cb2dbfa7617dd0cef118b25790f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:23:50 +0100 Subject: [PATCH 11/37] Add semicolon if nothing is returned --- src/cpon.rs | 2 +- src/json.rs | 4 ++-- src/metamap.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cpon.rs b/src/cpon.rs index 389445f..f5b79a4 100644 --- a/src/cpon.rs +++ b/src/cpon.rs @@ -385,7 +385,7 @@ impl<'a, R> CponReader<'a, R> let hi = b; let lo = self.get_byte()?; let b = self.decode_hex_byte(hi)? * 16 + self.decode_hex_byte(lo)?; - buff.push(b) + buff.push(b); }, } } diff --git a/src/json.rs b/src/json.rs index db89b53..204df7c 100644 --- a/src/json.rs +++ b/src/json.rs @@ -326,11 +326,11 @@ where R: Read let mut utf8 = [0; 4]; let s = ch.encode_utf8(&mut utf8); for b in s.as_bytes() { - buff.push(*b) + buff.push(*b); } } _ => { - buff.push(b) + buff.push(b); }, } } diff --git a/src/metamap.rs b/src/metamap.rs index 37ce3d2..e329ba8 100644 --- a/src/metamap.rs +++ b/src/metamap.rs @@ -79,7 +79,7 @@ impl MetaMap { None => { let key = key.make_key(); let key = MetaKey::from(&key); - self.0.push(MetaKeyVal{key, value }) + self.0.push(MetaKeyVal{key, value }); }, Some(ix) => self.0.get_mut(ix).expect("The value has been found with .find()").value = value, } From 280182a143eacb4d40290f222fc0f492165dc910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:28:25 +0100 Subject: [PATCH 12/37] Use String::clone instead of String::to_string --- src/rpcvalue.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpcvalue.rs b/src/rpcvalue.rs index aa44465..0bdebcd 100644 --- a/src/rpcvalue.rs +++ b/src/rpcvalue.rs @@ -124,7 +124,7 @@ impl From for Value { } impl From<&String> for Value { fn from(val: &String) -> Self { - Value::String(Box::new(val.to_string())) + Value::String(Box::new(val.clone())) } } From 4c223a2db19d95297ff70a9f670c8eadc1958adf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:30:08 +0100 Subject: [PATCH 13/37] Remove explicit into_iter for for-loops --- src/rpcvalue.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rpcvalue.rs b/src/rpcvalue.rs index 0bdebcd..dd17d0d 100644 --- a/src/rpcvalue.rs +++ b/src/rpcvalue.rs @@ -858,7 +858,7 @@ where fn try_from(value: RpcValue) -> Result { let mut res = Self::new(); - for (key, val) in Map::try_from(value)?.into_iter() { + for (key, val) in Map::try_from(value)? { let val = val.try_into().map_err(|e| format!("Wrong item at key `{key}`: {e}"))?; res.insert(key,val); } @@ -893,7 +893,7 @@ where fn try_from(value: RpcValue) -> Result { let mut res = Self::new(); - for (key, val) in IMap::try_from(value)?.into_iter() { + for (key, val) in IMap::try_from(value)? { res.insert( key, val.try_into() From 91906352501308766c9ca6cc71236656ef72dc2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:31:13 +0100 Subject: [PATCH 14/37] Improve () matching --- src/rpcvalue.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpcvalue.rs b/src/rpcvalue.rs index dd17d0d..f571949 100644 --- a/src/rpcvalue.rs +++ b/src/rpcvalue.rs @@ -100,7 +100,7 @@ impl Value { } impl From<()> for Value { - fn from(_: ()) -> Self { + fn from((): ()) -> Self { Value::Null } } From 938a45a6e104a5234ba48372fe1f39be9bcd986d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:32:12 +0100 Subject: [PATCH 15/37] Add must_use attrs --- src/datetime.rs | 5 +++++ src/decimal.rs | 1 + 2 files changed, 6 insertions(+) diff --git a/src/datetime.rs b/src/datetime.rs index b43ca78..a3334a0 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -182,22 +182,27 @@ impl DateTime { s } + #[must_use] pub fn add_days(&self, days: i64) -> Self { let (msec, offset) = self.epoc_msec_utc_offset(); Self::from_epoch_msec_tz(msec + (days * 24 * 60 * 60 * 1000), offset) } + #[must_use] pub fn add_hours(&self, hours: i64) -> Self { let (msec, offset) = self.epoc_msec_utc_offset(); Self::from_epoch_msec_tz(msec + (hours * 60 * 60 * 1000), offset) } + #[must_use] pub fn add_minutes(&self, minutes: i64) -> Self { let (msec, offset) = self.epoc_msec_utc_offset(); Self::from_epoch_msec_tz(msec + (minutes * 60 * 1000), offset) } + #[must_use] pub fn add_seconds(&self, seconds: i64) -> Self { let (msec, offset) = self.epoc_msec_utc_offset(); Self::from_epoch_msec_tz(msec + (seconds * 1000), offset) } + #[must_use] pub fn add_millis(&self, millis: i64) -> Self { let (msec, offset) = self.epoc_msec_utc_offset(); Self::from_epoch_msec_tz(msec + millis, offset) diff --git a/src/decimal.rs b/src/decimal.rs index 1bb701b..92946c7 100644 --- a/src/decimal.rs +++ b/src/decimal.rs @@ -11,6 +11,7 @@ impl Decimal { n |= i64::from(exponent) & 0xff; Decimal(n) } + #[must_use] pub fn normalize(&self) -> Decimal { let (mut mantissa, mut exponent) = self.decode(); if mantissa == 0 { From 5a2940380cb5d5992d5e8d62ac39cdd7714c90ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:34:52 +0100 Subject: [PATCH 16/37] Remove redundant match arms --- src/cpon.rs | 12 +++--------- src/textrdwr.rs | 1 - 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/cpon.rs b/src/cpon.rs index f5b79a4..886249d 100644 --- a/src/cpon.rs +++ b/src/cpon.rs @@ -42,9 +42,7 @@ impl<'a, W> CponWriter<'a, W> } for it in lst.iter() { match &it.value { - Value::List(_) => return false, - Value::Map(_) => return false, - Value::IMap(_) => return false, + Value::List(_) | Value::Map(_) | Value::IMap(_) => return false, _ => continue, } } @@ -62,9 +60,7 @@ impl<'a, W> CponWriter<'a, W> match iter.next() { Some(val) => { match &val.1.value { - Value::List(_) => return false, - Value::Map(_) => return false, - Value::IMap(_) => return false, + Value::List(_) | Value::Map(_) | Value::IMap(_) => return false, _ => (), } }, @@ -81,9 +77,7 @@ impl<'a, W> CponWriter<'a, W> } for k in map.0.iter() { match &k.value.value { - Value::List(_) => return false, - Value::Map(_) => return false, - Value::IMap(_) => return false, + Value::List(_) | Value::Map(_) | Value::IMap(_) => return false, _ => continue, } } diff --git a/src/textrdwr.rs b/src/textrdwr.rs index 4a5e5c8..2d1f148 100644 --- a/src/textrdwr.rs +++ b/src/textrdwr.rs @@ -126,7 +126,6 @@ pub trait TextReader : Reader { loop { let b = self.peek_byte(); let digit = match b { - 0 => break, b'+' | b'-' => { if n != 0 { break; From ace687f587e226b88755c84b9d1e7b08c37c509c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:36:43 +0100 Subject: [PATCH 17/37] Do not pass Decimal/DateTime by reference It's not worth it. --- src/chainpack.rs | 8 ++++---- src/cpon.rs | 6 +++--- src/json.rs | 6 +++--- src/textrdwr.rs | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/chainpack.rs b/src/chainpack.rs index 8ac2ea5..885d864 100644 --- a/src/chainpack.rs +++ b/src/chainpack.rs @@ -196,13 +196,13 @@ where self.write_bytes(&bytes)?; Ok(self.byte_writer.count() - cnt) } - fn write_decimal(&mut self, decimal: &Decimal) -> WriteResult { + fn write_decimal(&mut self, decimal: Decimal) -> WriteResult { let cnt = self.write_byte(PackingSchema::Decimal as u8)?; self.write_int_data(decimal.mantissa())?; self.write_int_data(i64::from(decimal.exponent()))?; Ok(self.byte_writer.count() - cnt) } - fn write_datetime(&mut self, dt: &DateTime) -> WriteResult { + fn write_datetime(&mut self, dt: DateTime) -> WriteResult { let cnt = self.write_byte(PackingSchema::DateTime as u8)?; let mut msecs = dt.epoch_msec() - SHV_EPOCH_MSEC; let offset = (dt.utc_offset() / 60 / 15) & 0x7F; @@ -307,8 +307,8 @@ where Value::String(s) => self.write_string(s)?, Value::Blob(b) => self.write_blob(b)?, Value::Double(n) => self.write_double(*n)?, - Value::Decimal(d) => self.write_decimal(d)?, - Value::DateTime(d) => self.write_datetime(d)?, + Value::Decimal(d) => self.write_decimal(*d)?, + Value::DateTime(d) => self.write_datetime(*d)?, Value::List(lst) => self.write_list(lst)?, Value::Map(map) => self.write_map(map)?, Value::IMap(map) => self.write_imap(map)?, diff --git a/src/cpon.rs b/src/cpon.rs index 886249d..a376c2b 100644 --- a/src/cpon.rs +++ b/src/cpon.rs @@ -197,7 +197,7 @@ impl<'a, W> CponWriter<'a, W> self.write_byte(b'"')?; Ok(self.byte_writer.count() - cnt) } - fn write_datetime(&mut self, dt: &DateTime) -> WriteResult { + fn write_datetime(&mut self, dt: DateTime) -> WriteResult { let cnt = self.write_bytes("d\"".as_bytes())?; let s = dt.to_iso_string_opt(&ToISOStringOptions { include_millis: IncludeMilliseconds::WhenNonZero, @@ -330,8 +330,8 @@ impl Writer for CponWriter<'_, W> Value::String(s) => self.write_string(s), Value::Blob(b) => self.write_blob(b), Value::Double(n) => self.write_double(*n), - Value::Decimal(d) => self.write_decimal(d), - Value::DateTime(d) => self.write_datetime(d), + Value::Decimal(d) => self.write_decimal(*d), + Value::DateTime(d) => self.write_datetime(*d), Value::List(lst) => self.write_list(lst), Value::Map(map) => self.write_map(map), Value::IMap(map) => self.write_imap(map), diff --git a/src/json.rs b/src/json.rs index 204df7c..c988028 100644 --- a/src/json.rs +++ b/src/json.rs @@ -64,7 +64,7 @@ impl<'a, W> JsonWriter<'a, W> self.write_byte(b'"')?; Ok(self.byte_writer.count() - cnt) } - fn write_datetime(&mut self, dt: &DateTime) -> WriteResult { + fn write_datetime(&mut self, dt: DateTime) -> WriteResult { let cnt = self.byte_writer.count(); self.add_wrap_type_tag("DateTime")?; self.write_byte(b'"')?; @@ -213,8 +213,8 @@ where W: Write Value::String(s) => self.write_string(s), Value::Blob(b) => self.write_blob(b), Value::Double(n) => self.write_double(*n), - Value::Decimal(d) => self.write_decimal(d), - Value::DateTime(d) => self.write_datetime(d), + Value::Decimal(d) => self.write_decimal(*d), + Value::DateTime(d) => self.write_datetime(*d), Value::List(lst) => self.write_list(lst), Value::Map(map) => self.write_map(map), Value::IMap(map) => self.write_imap(map), diff --git a/src/textrdwr.rs b/src/textrdwr.rs index 2d1f148..d5fdbf2 100644 --- a/src/textrdwr.rs +++ b/src/textrdwr.rs @@ -16,7 +16,7 @@ pub trait TextWriter : Writer { let cnt = self.write_bytes(s.as_bytes())?; Ok(self.write_count() - cnt) } - fn write_decimal(&mut self, decimal: &Decimal) -> WriteResult { + fn write_decimal(&mut self, decimal: Decimal) -> WriteResult { let s = decimal.to_cpon_string(); let cnt = self.write_bytes(s.as_bytes())?; Ok(self.write_count() - cnt) From 4ea72a809e8ced6f08fbbbf792fee5a6d684d37e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:52:08 +0100 Subject: [PATCH 18/37] Fix needless_pass_by_value --- src/metamap.rs | 3 +++ src/rpcvalue.rs | 1 + 2 files changed, 4 insertions(+) diff --git a/src/metamap.rs b/src/metamap.rs index e329ba8..0a64658 100644 --- a/src/metamap.rs +++ b/src/metamap.rs @@ -72,6 +72,7 @@ impl MetaMap { pub fn len(&self) -> usize { self.0.len() } + #[expect(clippy::needless_pass_by_value, reason = "GetIndex is implemented for a lot of types, and some might not be owned")] pub fn insert(&mut self, key: I, value: RpcValue) -> &mut Self where I: GetIndex { @@ -85,6 +86,7 @@ impl MetaMap { } self } + #[expect(clippy::needless_pass_by_value, reason = "GetIndex is implemented for a lot of types, and some might not be owned")] pub fn remove(&mut self, key: I) -> Option where I: GetIndex { @@ -93,6 +95,7 @@ impl MetaMap { Some(ix) => Some(self.0.remove(ix).value), } } + #[expect(clippy::needless_pass_by_value, reason = "GetIndex is implemented for a lot of types, and some might not be owned")] pub fn get(&self, key: I) -> Option<&RpcValue> where I: GetIndex { diff --git a/src/rpcvalue.rs b/src/rpcvalue.rs index f571949..e2693cd 100644 --- a/src/rpcvalue.rs +++ b/src/rpcvalue.rs @@ -1086,6 +1086,7 @@ impl RpcValue { _ => EMPTY_IMAP.get_or_init(IMap::new), } } + #[expect(clippy::needless_pass_by_value, reason = "GetIndex is implemented for a lot of types, and some might not be owned")] pub fn get(&self, key: I) -> Option<&RpcValue> where I: GetIndex, From ffa540d819b3ffdb3c40690ac94aa284c34bf953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:53:30 +0100 Subject: [PATCH 19/37] Remove redundant match --- src/cpon.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/cpon.rs b/src/cpon.rs index a376c2b..a5600d2 100644 --- a/src/cpon.rs +++ b/src/cpon.rs @@ -41,9 +41,8 @@ impl<'a, W> CponWriter<'a, W> return false; } for it in lst.iter() { - match &it.value { - Value::List(_) | Value::Map(_) | Value::IMap(_) => return false, - _ => continue, + if matches!(&it.value, Value::List(_) | Value::Map(_) | Value::IMap(_)) { + return false; } } true @@ -76,9 +75,8 @@ impl<'a, W> CponWriter<'a, W> return false; } for k in map.0.iter() { - match &k.value.value { - Value::List(_) | Value::Map(_) | Value::IMap(_) => return false, - _ => continue, + if matches!(&k.value.value, Value::List(_) | Value::Map(_) | Value::IMap(_)) { + return false; } } true From c832added03f209da69404d755f6e26ab94b562e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:55:56 +0100 Subject: [PATCH 20/37] Remove unwrap --- src/datetime.rs | 2 +- src/serde/de.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/datetime.rs b/src/datetime.rs index a3334a0..7095033 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -138,7 +138,7 @@ impl DateTime { } pub fn to_chrono_datetime(&self) -> chrono::DateTime { let offset = match FixedOffset::east_opt(self.utc_offset()) { - None => {FixedOffset::east_opt(0).unwrap()} + None => {FixedOffset::east_opt(0).expect("Zero is within the range")} Some(o) => {o} }; chrono::DateTime::from_naive_utc_and_offset(self.to_chrono_naivedatetime(), offset) diff --git a/src/serde/de.rs b/src/serde/de.rs index efadc17..4ec63af 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -269,7 +269,7 @@ impl<'de, 'a> serde::Deserializer<'de> for ValueDeserializer<'a> { match self.value { // Externally tagged (e.g. { "Variant": {...} }) Value::Map(map) if map.len() == 1 => { - let (variant, value) = map.iter().next().unwrap(); + let (variant, value) = map.iter().next().expect("len() is 1"); let enum_access = SimpleEnumAccess { variant, value: Some(&value.value) From 5b900b8d3c5790e8c64e39d87434d2406def84c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 16:15:14 +0100 Subject: [PATCH 21/37] Prefer expect over allow --- src/chainpack.rs | 3 +-- src/cpon.rs | 3 +-- src/json.rs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/chainpack.rs b/src/chainpack.rs index 885d864..033acc2 100644 --- a/src/chainpack.rs +++ b/src/chainpack.rs @@ -8,9 +8,8 @@ use std::collections::BTreeMap; use std::io; use std::io::{Read, Write}; -#[allow(clippy::upper_case_acronyms)] +#[expect(clippy::upper_case_acronyms, reason = "We just want these")] #[warn(non_camel_case_types)] -#[allow(dead_code)] pub enum PackingSchema { Null = 128, UInt, diff --git a/src/cpon.rs b/src/cpon.rs index a5600d2..11d32b4 100644 --- a/src/cpon.rs +++ b/src/cpon.rs @@ -628,8 +628,7 @@ mod test assert_eq!(RpcValue::from_cpon(r#"d"2022-01-02T12:59:06Z""#).unwrap().as_datetime(), DateTime::from_datetime(&dt)); - // Allow in tests - #[allow(clippy::too_many_arguments)] + #[expect(clippy::too_many_arguments, reason = "Allow in tests")] fn dt_from_ymd_hms_milli_tz_offset(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32, milli: i64, tz_offset: i32) -> chrono::DateTime { if let LocalResult::Single(dt) = FixedOffset::east_opt(tz_offset).unwrap() .with_ymd_and_hms(year, month, day, hour, min, sec) { diff --git a/src/json.rs b/src/json.rs index c988028..f2c8794 100644 --- a/src/json.rs +++ b/src/json.rs @@ -509,7 +509,7 @@ mod test const MINUTE: i32 = 60; const HOUR: i32 = 60 * MINUTE; - #[allow(clippy::too_many_arguments)] + #[expect(clippy::too_many_arguments, reason = "Allow in tests")] fn dt_from_ymd_hms_milli_tz_offset(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32, milli: i64, tz_offset: i32) -> chrono::DateTime { if let LocalResult::Single(dt) = FixedOffset::east_opt(tz_offset).unwrap() .with_ymd_and_hms(year, month, day, hour, min, sec) { From 5d54f267d1a03095fac3eec6447fe0c78455aa8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 16:26:52 +0100 Subject: [PATCH 22/37] Remove unnecessary to_string() --- src/bin/cp2cp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/cp2cp.rs b/src/bin/cp2cp.rs index 0fdd96c..a2371dd 100644 --- a/src/bin/cp2cp.rs +++ b/src/bin/cp2cp.rs @@ -110,7 +110,7 @@ fn process_chainpack_rpc_block(mut reader: Box) -> ! { } match rd.read() { Ok(rv) => { - result.cpon = rv.to_cpon().to_string(); + result.cpon = rv.to_cpon(); exit_with_result_and_code(&result, None); } Err(e) => { From 7a100ba04fbdd6cb8e191fe10175120f198e0178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 16:29:04 +0100 Subject: [PATCH 23/37] Use chars instead strings where possible --- src/json.rs | 4 ++-- tests/test_cp2cp.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/json.rs b/src/json.rs index f2c8794..fb3e8d7 100644 --- a/src/json.rs +++ b/src/json.rs @@ -429,7 +429,7 @@ mod test let rv2 = RpcValue::from(val); assert_eq!(rv1, rv2); let json2 = rv1.to_json(); - assert_eq!(&json.replace(" ", ""), &json2); + assert_eq!(&json.replace(' ', ""), &json2); } fn test_cpon_cross_check(json: &str, cpon: &str) { let json1 = fix_tags(json); @@ -438,7 +438,7 @@ mod test assert_eq!(rv1, rv2); let json2 = rv1.to_json(); // println!("{json2} <=> {json2}"); - assert_eq!(&json1.replace(" ", ""), &json2); + assert_eq!(&json1.replace(' ', ""), &json2); } #[test] fn test_string() { diff --git a/tests/test_cp2cp.rs b/tests/test_cp2cp.rs index 05e2a6a..a48aff4 100644 --- a/tests/test_cp2cp.rs +++ b/tests/test_cp2cp.rs @@ -27,7 +27,7 @@ mod test { let output = run_cp2cp(data)?; let exit_code = output.status.code().unwrap(); let output = String::from_utf8(output.stdout).map_err(|e| e.to_string())?; - let mut lines = output.split("\n"); + let mut lines = output.split('\n'); let block_length = lines.next().unwrap().parse::().unwrap(); let frame_length = lines.next().unwrap().parse::().unwrap(); let proto = lines.next().unwrap().parse::().unwrap(); From 79e116a2157467c1b9797500817e4fc5d98e38bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 16:32:50 +0100 Subject: [PATCH 24/37] Elide some lifetimes --- src/serde/de.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/serde/de.rs b/src/serde/de.rs index 4ec63af..59024dc 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -11,7 +11,7 @@ pub struct ValueDeserializer<'a> { pub value: &'a Value, } -impl<'de, 'a> IntoDeserializer<'de> for ValueDeserializer<'a> { +impl IntoDeserializer<'_> for ValueDeserializer<'_> { type Deserializer = Self; fn into_deserializer(self) -> Self::Deserializer { @@ -19,7 +19,7 @@ impl<'de, 'a> IntoDeserializer<'de> for ValueDeserializer<'a> { } } -impl<'de, 'a> serde::Deserializer<'de> for ValueDeserializer<'a> { +impl<'de> serde::Deserializer<'de> for ValueDeserializer<'_> { type Error = de::value::Error; fn deserialize_any(self, visitor: V) -> Result @@ -326,7 +326,7 @@ struct SimpleEnumAccess<'a> { value: Option<&'a Value>, } -impl<'a, 'de> EnumAccess<'de> for SimpleEnumAccess<'a> { +impl<'de> EnumAccess<'de> for SimpleEnumAccess<'_> { type Error = serde::de::value::Error; type Variant = Self; @@ -337,7 +337,7 @@ impl<'a, 'de> EnumAccess<'de> for SimpleEnumAccess<'a> { } } -impl<'a, 'de> VariantAccess<'de> for SimpleEnumAccess<'a> { +impl<'de> VariantAccess<'de> for SimpleEnumAccess<'_> { type Error = serde::de::value::Error; fn unit_variant(self) -> Result<(), Self::Error> { @@ -368,7 +368,7 @@ impl<'a, 'de> VariantAccess<'de> for SimpleEnumAccess<'a> { struct UntaggedEnumAccess<'a>(&'a Value); -impl<'a, 'de> EnumAccess<'de> for UntaggedEnumAccess<'a> { +impl<'de> EnumAccess<'de> for UntaggedEnumAccess<'_> { type Error = serde::de::value::Error; type Variant = Self; @@ -382,7 +382,7 @@ impl<'a, 'de> EnumAccess<'de> for UntaggedEnumAccess<'a> { } } -impl<'a, 'de> VariantAccess<'de> for UntaggedEnumAccess<'a> { +impl<'de> VariantAccess<'de> for UntaggedEnumAccess<'_> { type Error = serde::de::value::Error; fn unit_variant(self) -> Result<(), Self::Error> { @@ -412,7 +412,7 @@ impl<'de> serde::Deserialize<'de> for crate::DateTime { { struct DateTimeVisitor; - impl<'de> Visitor<'de> for DateTimeVisitor { + impl Visitor<'_> for DateTimeVisitor { type Value = crate::DateTime; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -485,7 +485,7 @@ impl<'de> serde::Deserialize<'de> for Blob { struct BlobVisitor; - impl<'de> Visitor<'de> for BlobVisitor { + impl Visitor<'_> for BlobVisitor { type Value = Blob; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { From 6ec82af9a22805a5c0e0984a804d63efe471fd52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 16:36:10 +0100 Subject: [PATCH 25/37] cp2cp: Allow stderr/stdout/exit --- src/bin/cp2cp.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/cp2cp.rs b/src/bin/cp2cp.rs index a2371dd..1586b86 100644 --- a/src/bin/cp2cp.rs +++ b/src/bin/cp2cp.rs @@ -1,3 +1,4 @@ +#![allow(clippy::print_stderr, clippy::print_stdout, clippy::exit, reason = "Fine for a binary")] use clap::Parser; use log::LevelFilter; use shvproto::reader::ReadErrorReason; From 93c56e1514ac45beb93fed70e6b6b0ef6bcf469f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 16:40:10 +0100 Subject: [PATCH 26/37] Ignore warning --- src/bin/cp2cp.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/cp2cp.rs b/src/bin/cp2cp.rs index 1586b86..fd8405e 100644 --- a/src/bin/cp2cp.rs +++ b/src/bin/cp2cp.rs @@ -16,6 +16,7 @@ use jaq_all::{jaq_core::{Ctx, Vars, data::JustLut}, jaq_std}; #[derive(Parser, Debug)] #[structopt(name = "cp2cp", version = env!("CARGO_PKG_VERSION"), author = env!("CARGO_PKG_AUTHORS"), about = "ChainPack to Cpon and back utility")] +#[expect(clippy::struct_excessive_bools, reason = "Fine for cli args")] struct Cli { #[arg(short, long, help = "Cpon indentation string")] indent: Option, From 6f5f8fc7d5941f665e4c2d4496ede065bac91961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 22:57:21 +0100 Subject: [PATCH 27/37] Remove unneccessary borrow --- src/bin/cp2cp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/cp2cp.rs b/src/bin/cp2cp.rs index fd8405e..014608c 100644 --- a/src/bin/cp2cp.rs +++ b/src/bin/cp2cp.rs @@ -61,7 +61,7 @@ fn print_option(n: Option) { } } fn exit_with_result_and_code(result: &ChainPackRpcBlockResult, error: Option) -> ! { - let exit_code = if let Some(error) = &error { + let exit_code = if let Some(error) = error { match error { ReadErrorReason::UnexpectedEndOfStream => CODE_NOT_ENOUGH_DATA, ReadErrorReason::NotEnoughPrecision => CODE_READ_ERROR, From 48b4d453cd057da9a72c2d3fc030b636d51a2e20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 22:57:29 +0100 Subject: [PATCH 28/37] Do not pass by value --- src/chainpack.rs | 76 ++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/chainpack.rs b/src/chainpack.rs index 033acc2..de4baad 100644 --- a/src/chainpack.rs +++ b/src/chainpack.rs @@ -634,10 +634,10 @@ fn chainpack_to_rpcvalue(data: &str) -> Result { } #[cfg(test)] -fn rpcvalue_to_chainpack(value: RpcValue) -> String { +fn rpcvalue_to_chainpack(value: &RpcValue) -> String { let mut data = Vec::new(); let mut wr = ChainPackWriter::new(&mut data); - wr.write(&value).expect("Write must work"); + wr.write(value).expect("Write must work"); hex::encode(data).to_uppercase() } @@ -652,23 +652,23 @@ fn test_int() { assert_eq!(chainpack_to_rpcvalue("82F3138083FD18A37C").unwrap(), 5_489_328_932_823_932_i64.into()); assert_eq!(chainpack_to_rpcvalue("82F47FFFFFFFFFFFFFFF").unwrap(), 9_223_372_036_854_775_807_i64.into()); - assert_eq!(rpcvalue_to_chainpack(120_u64.into()), "8178"); - assert_eq!(rpcvalue_to_chainpack(2_u64.into()), "02"); - assert_eq!(rpcvalue_to_chainpack(508_u64.into()), "8181FC"); - assert_eq!(rpcvalue_to_chainpack(1_048_570_u64.into()), "81CFFFFA"); - assert_eq!(rpcvalue_to_chainpack(33_554_400_u64.into()), "81E1FFFFE0"); - assert_eq!(rpcvalue_to_chainpack(5_489_328_932_823_932_i64.into()), "82F3138083FD18A37C"); - assert_eq!(rpcvalue_to_chainpack(9_223_372_036_854_775_807_i64.into()), "82F47FFFFFFFFFFFFFFF"); + assert_eq!(rpcvalue_to_chainpack(&120_u64.into()), "8178"); + assert_eq!(rpcvalue_to_chainpack(&2_u64.into()), "02"); + assert_eq!(rpcvalue_to_chainpack(&508_u64.into()), "8181FC"); + assert_eq!(rpcvalue_to_chainpack(&1_048_570_u64.into()), "81CFFFFA"); + assert_eq!(rpcvalue_to_chainpack(&33_554_400_u64.into()), "81E1FFFFE0"); + assert_eq!(rpcvalue_to_chainpack(&5_489_328_932_823_932_i64.into()), "82F3138083FD18A37C"); + assert_eq!(rpcvalue_to_chainpack(&9_223_372_036_854_775_807_i64.into()), "82F47FFFFFFFFFFFFFFF"); // Negative int assert_eq!(chainpack_to_rpcvalue("82E9FFFFE0").unwrap(), (-33_554_400).into()); - assert_eq!(rpcvalue_to_chainpack((-33_554_400).into()), "82E9FFFFE0"); + assert_eq!(rpcvalue_to_chainpack(&(-33_554_400).into()), "82E9FFFFE0"); } #[test] fn test_string() { assert_eq!(chainpack_to_rpcvalue("860541484F4A21").unwrap(), "AHOJ!".into()); - assert_eq!(rpcvalue_to_chainpack("AHOJ!".into()), "860541484F4A21"); + assert_eq!(rpcvalue_to_chainpack(&"AHOJ!".into()), "860541484F4A21"); // Invalid UTF-8 string assert!(chainpack_to_rpcvalue("8602C328").is_err()); @@ -677,16 +677,16 @@ fn test_string() { #[test] fn test_true_false_packing_schema() { assert_eq!(chainpack_to_rpcvalue("FE").unwrap(), true.into()); - assert_eq!(rpcvalue_to_chainpack(true.into()), "FE"); + assert_eq!(rpcvalue_to_chainpack(&true.into()), "FE"); assert_eq!(chainpack_to_rpcvalue("FD").unwrap(), false.into()); - assert_eq!(rpcvalue_to_chainpack(false.into()), "FD"); + assert_eq!(rpcvalue_to_chainpack(&false.into()), "FD"); } #[test] fn test_cstring() { assert_eq!(chainpack_to_rpcvalue("8E41484F4A2100").unwrap(), "AHOJ!".into()); - assert_eq!(rpcvalue_to_chainpack("AHOJ!".into()), "860541484F4A21"); + assert_eq!(rpcvalue_to_chainpack(&"AHOJ!".into()), "860541484F4A21"); // Invalid UTF-8 string assert!(chainpack_to_rpcvalue("8EC32800").is_err()); @@ -696,14 +696,14 @@ fn test_cstring() { fn test_blob() { let blob = vec![170u8; 10]; assert_eq!(chainpack_to_rpcvalue("850AAAAAAAAAAAAAAAAAAAAA").unwrap(), blob.clone().into()); - assert_eq!(rpcvalue_to_chainpack(blob.into()), "850AAAAAAAAAAAAAAAAAAAAA"); + assert_eq!(rpcvalue_to_chainpack(&blob.into()), "850AAAAAAAAAAAAAAAAAAAAA"); } #[test] fn test_list() { let list = crate::make_list!["a", 123, true, crate::make_list![1, 2, 3], RpcValue::null()]; assert_eq!(chainpack_to_rpcvalue("8886016182807BFE88414243FF80FF").unwrap(), RpcValue::from(list.clone())); - assert_eq!(rpcvalue_to_chainpack(RpcValue::from(list)), "8886016182807BFE88414243FF80FF"); + assert_eq!(rpcvalue_to_chainpack(&RpcValue::from(list)), "8886016182807BFE88414243FF80FF"); } #[test] @@ -714,7 +714,7 @@ fn test_map() { "foo" => vec![11,12,13] }; assert_eq!(chainpack_to_rpcvalue("89860362617242860362617A438603666F6F884B4C4DFFFF").unwrap(), map.clone().into()); - assert_eq!(rpcvalue_to_chainpack(map.into()), "89860362617242860362617A438603666F6F884B4C4DFFFF"); + assert_eq!(rpcvalue_to_chainpack(&map.into()), "89860362617242860362617A438603666F6F884B4C4DFFFF"); // Invalid key assert_eq!(chainpack_to_rpcvalue("898200").unwrap_err().msg, "ChainPack read error - Invalid Map key '0'"); @@ -729,7 +729,7 @@ fn test_imap() { }; assert_eq!(chainpack_to_rpcvalue("8A418603666F6F42860362617282814D4FFF").unwrap(), imap.clone().into()); - assert_eq!(rpcvalue_to_chainpack(imap.into()), "8A418603666F6F42860362617282814D4FFF"); + assert_eq!(rpcvalue_to_chainpack(&imap.into()), "8A418603666F6F42860362617282814D4FFF"); // Invalid key assert_eq!(chainpack_to_rpcvalue("8A8603626172").unwrap_err().msg, "ChainPack read error - Invalid IMap key '\"bar\"'"); @@ -756,37 +756,37 @@ fn test_datetime() { assert_eq!(chainpack_to_rpcvalue("8DF182D3308815").unwrap(), DateTime::from_epoch_msec_tz(1_493_832_123_000, -5400).into()); assert_eq!(chainpack_to_rpcvalue("8DF1961334BEB4").unwrap(), DateTime::from_epoch_msec_tz(1_493_826_723_923, 0).into()); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_517_529_600_001, 0).into()), "8D04"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_517_529_600_001, 3600).into()), "8D8211"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_543_708_800_000, 0).into()), "8DE63DDA02"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_514_764_800_000, 0).into()), "8DE8A8BFFE"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_546_300_800_000, 0).into()), "8DE6DC0E02"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_577_836_800_000, 0).into()), "8DF00E60DC02"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_609_459_200_000, 0).into()), "8DF015EAF002"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_924_992_000_000, 0).into()), "8DF061258802"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(2_240_611_200_000, 0).into()), "8DF100AC656602"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(2_246_004_900_000, -36900).into()), "8DF156D74D495F"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(2_246_004_900_123, -36900).into()), "8DF301533905E2375D"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(0, 0).into()), "8DF18169CEA7FE"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_493_790_723_000, 0).into()), "8DEDA8E7F2"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_493_826_723_923, 0).into()), "8DF1961334BEB4"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_493_790_751_123, 36000).into()), "8DF28B0DE42CD95F"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_493_826_723_000, 0).into()), "8DEDA6B572"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_493_832_123_000, -5400).into()), "8DF182D3308815"); - assert_eq!(rpcvalue_to_chainpack(DateTime::from_epoch_msec_tz(1_493_826_723_923, 0).into()), "8DF1961334BEB4"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_517_529_600_001, 0).into()), "8D04"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_517_529_600_001, 3600).into()), "8D8211"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_543_708_800_000, 0).into()), "8DE63DDA02"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_514_764_800_000, 0).into()), "8DE8A8BFFE"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_546_300_800_000, 0).into()), "8DE6DC0E02"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_577_836_800_000, 0).into()), "8DF00E60DC02"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_609_459_200_000, 0).into()), "8DF015EAF002"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_924_992_000_000, 0).into()), "8DF061258802"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(2_240_611_200_000, 0).into()), "8DF100AC656602"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(2_246_004_900_000, -36900).into()), "8DF156D74D495F"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(2_246_004_900_123, -36900).into()), "8DF301533905E2375D"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(0, 0).into()), "8DF18169CEA7FE"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_493_790_723_000, 0).into()), "8DEDA8E7F2"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_493_826_723_923, 0).into()), "8DF1961334BEB4"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_493_790_751_123, 36000).into()), "8DF28B0DE42CD95F"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_493_826_723_000, 0).into()), "8DEDA6B572"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_493_832_123_000, -5400).into()), "8DF182D3308815"); + assert_eq!(rpcvalue_to_chainpack(&DateTime::from_epoch_msec_tz(1_493_826_723_923, 0).into()), "8DF1961334BEB4"); } #[test] fn test_double() { assert_eq!(chainpack_to_rpcvalue("830000000000C208B8").unwrap(), RpcValue::from(-9.094_583_978_896_067E-39_f64)); - assert_eq!(rpcvalue_to_chainpack(RpcValue::from(-9.094_583_978_896_067E-39_f64)), "830000000000C208B8"); + assert_eq!(rpcvalue_to_chainpack(&RpcValue::from(-9.094_583_978_896_067E-39_f64)), "830000000000C208B8"); } #[test] fn test_decimal() { let dec = crate::decimal::Decimal::new(0, 0); assert_eq!(chainpack_to_rpcvalue("8C0000").unwrap(), dec.into()); - assert_eq!(rpcvalue_to_chainpack(dec.into()), "8C0000"); + assert_eq!(rpcvalue_to_chainpack(&dec.into()), "8C0000"); } #[test] From 3b66bf6f022c01f88e6a1adb84f7c5d8945f3143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 16:42:38 +0100 Subject: [PATCH 29/37] Improve function calls --- libshvproto-macros/src/lib.rs | 9 ++------- src/bin/cp2cp.rs | 18 +++++++----------- src/datetime.rs | 6 ++---- src/jaq.rs | 8 ++++---- src/json.rs | 2 +- src/metamap.rs | 5 +---- src/serde/de.rs | 12 ++++-------- src/util.rs | 18 ++++++------------ tests/test_cp2cp.rs | 5 ++--- 9 files changed, 29 insertions(+), 54 deletions(-) diff --git a/libshvproto-macros/src/lib.rs b/libshvproto-macros/src/lib.rs index 15143fa..f5c3d05 100644 --- a/libshvproto-macros/src/lib.rs +++ b/libshvproto-macros/src/lib.rs @@ -48,8 +48,7 @@ fn get_field_name(field: &syn::Field) -> String { .and_then(|attr| attr.meta.require_name_value().ok()) .filter(|meta_name_value| meta_name_value.path.is_ident("field_name")) .map(|meta_name_value| if let syn::Expr::Lit(expr) = &meta_name_value.value { expr } else { panic!("Expected a string literal for 'field_name'") }) - .map(|literal| if let syn::Lit::Str(expr) = &literal.lit { expr.value() } else { panic!("Expected a string literal for 'field_name'") }) - .unwrap_or_else(|| field.ident.as_ref().unwrap().to_string().to_case(Case::Camel)) + .map_or_else(|| field.ident.as_ref().unwrap().to_string().to_case(Case::Camel), |literal| if let syn::Lit::Str(expr) = &literal.lit { expr.value() } else { panic!("Expected a string literal for 'field_name'") }) } fn field_to_initializers( @@ -63,11 +62,7 @@ fn field_to_initializers( let is_option = is_option(&field.ty); let struct_initializer; let rpcvalue_insert; - let identifier_at_value = if let Some(value) = from_value { - quote! { #value.#identifier } - } else { - quote! { #identifier } - }; + let identifier_at_value = from_value.map_or_else(|| quote! { #identifier }, |value| quote! { #value.#identifier }); if is_option { struct_initializer = quote!{ #identifier: match get_key(#field_name).ok() { diff --git a/src/bin/cp2cp.rs b/src/bin/cp2cp.rs index 014608c..67cc7d6 100644 --- a/src/bin/cp2cp.rs +++ b/src/bin/cp2cp.rs @@ -61,18 +61,14 @@ fn print_option(n: Option) { } } fn exit_with_result_and_code(result: &ChainPackRpcBlockResult, error: Option) -> ! { - let exit_code = if let Some(error) = error { - match error { - ReadErrorReason::UnexpectedEndOfStream => CODE_NOT_ENOUGH_DATA, - ReadErrorReason::NotEnoughPrecision => CODE_READ_ERROR, - ReadErrorReason::InvalidCharacter => { - eprintln!("Parse input error: {error:?}"); - CODE_READ_ERROR - } + let exit_code = error.map_or(CODE_SUCCESS, |error| match error { + ReadErrorReason::UnexpectedEndOfStream => CODE_NOT_ENOUGH_DATA, + ReadErrorReason::NotEnoughPrecision => CODE_READ_ERROR, + ReadErrorReason::InvalidCharacter => { + eprintln!("Parse input error: {error:?}"); + CODE_READ_ERROR } - } else { - CODE_SUCCESS - }; + }); print_option(result.block_length); print_option(result.frame_length); print_option(result.proto); diff --git a/src/datetime.rs b/src/datetime.rs index 7095033..36ad9bc 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -137,10 +137,8 @@ impl DateTime { chrono::DateTime::from_timestamp_millis(msec).unwrap_or_default().naive_utc() } pub fn to_chrono_datetime(&self) -> chrono::DateTime { - let offset = match FixedOffset::east_opt(self.utc_offset()) { - None => {FixedOffset::east_opt(0).expect("Zero is within the range")} - Some(o) => {o} - }; + let offset = FixedOffset::east_opt(self.utc_offset()) + .unwrap_or_else(|| FixedOffset::east_opt(0).expect("Zero is within the range")); chrono::DateTime::from_naive_utc_and_offset(self.to_chrono_naivedatetime(), offset) } pub fn to_iso_string(&self) -> String { diff --git a/src/jaq.rs b/src/jaq.rs index 3da3e3f..709f797 100644 --- a/src/jaq.rs +++ b/src/jaq.rs @@ -126,7 +126,7 @@ impl From>> for RpcValue { fn from(value: core::ops::Range>) -> Self { let kv = |(k, v): (&str, Option<_>)| v.map(|v| (k.to_owned(), v)); let kvs = [("start", value.start), ("end", value.end)]; - kvs.into_iter().flat_map(kv).collect::>().into() + kvs.into_iter().filter_map(kv).collect::>().into() } } @@ -179,7 +179,7 @@ impl jaq_all::jaq_std::ValT for RpcValue { impl jaq_all::jaq_core::ValT for RpcValue { fn from_num(n: &str) -> ValR { - Ok(n.parse::().map(RpcValue::from).unwrap_or(0_i64.into())) + Ok(n.parse::().map_or_else(|_| 0_i64.into(), RpcValue::from)) } fn from_map>(iter: I) -> ValR { @@ -235,8 +235,8 @@ impl jaq_all::jaq_core::ValT for RpcValue { #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss, reason = "We assume pointer size is 64-bit")] (Value::String(rv), Value::Int(i)) => Ok(rv.chars().nth(*i as usize).map(|cha| cha.to_string()).into()), #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss, reason = "We assume pointer size is 64-bit")] - (Value::List(list), Value::Int(i)) => Ok((*list).get(*i as usize).cloned().unwrap_or(RpcValue::null())), - (Value::Map(o), Value::String(key)) => Ok(o.get(key.as_str()).cloned().unwrap_or(RpcValue::null())), + (Value::List(list), Value::Int(i)) => Ok((*list).get(*i as usize).cloned().unwrap_or_else(RpcValue::null)), + (Value::Map(o), Value::String(key)) => Ok(o.get(key.as_str()).cloned().unwrap_or_else(RpcValue::null)), (_s, _) => Err(Error::typ(self, "")), } } diff --git a/src/json.rs b/src/json.rs index fb3e8d7..5b3c0f2 100644 --- a/src/json.rs +++ b/src/json.rs @@ -322,7 +322,7 @@ where R: Read hex.push(self.get_byte()? as char); hex.push(self.get_byte()? as char); let code_point = u32::from_str_radix(&hex, 16).map_err(|e| self.make_error(&format!("Invalid unicode escape sequence: {hex:?} - {e}"), ReadErrorReason::InvalidCharacter))?; - let ch = char::from_u32(code_point).ok_or(self.make_error(&format!("Invalid code point: {code_point}"), ReadErrorReason::InvalidCharacter))?; + let ch = char::from_u32(code_point).ok_or_else(|| self.make_error(&format!("Invalid code point: {code_point}"), ReadErrorReason::InvalidCharacter))?; let mut utf8 = [0; 4]; let s = ch.encode_utf8(&mut utf8); for b in s.as_bytes() { diff --git a/src/metamap.rs b/src/metamap.rs index 0a64658..8b7f4dc 100644 --- a/src/metamap.rs +++ b/src/metamap.rs @@ -99,10 +99,7 @@ impl MetaMap { pub fn get(&self, key: I) -> Option<&RpcValue> where I: GetIndex { - match self.find(&key) { - Some(ix) => Some(&self.0.get(ix).expect("The value has been found with .find()").value), - None => None, - } + self.find(&key).map(|ix| &self.0.get(ix).expect("The value has been found with .find()").value) } fn find(&self, key: &I) -> Option where I: GetIndex diff --git a/src/serde/de.rs b/src/serde/de.rs index 59024dc..e5c3930 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -341,18 +341,14 @@ impl<'de> VariantAccess<'de> for SimpleEnumAccess<'_> { type Error = serde::de::value::Error; fn unit_variant(self) -> Result<(), Self::Error> { - match self.value { - Some(v) => serde::Deserialize::deserialize(ValueDeserializer { value: v }), - None => Ok(()), - } + self.value + .map_or(Ok(()), |v| serde::Deserialize::deserialize(ValueDeserializer { value: v })) } fn newtype_variant_seed(self, seed: T) -> Result where T: DeserializeSeed<'de> { - match self.value { - Some(v) => seed.deserialize(ValueDeserializer { value: v }), - None => Err(de::Error::custom("Expected value for newtype variant")), - } + self.value + .map_or_else(|| Err(de::Error::custom("Expected value for newtype variant")), |v| seed.deserialize(ValueDeserializer { value: v })) } fn tuple_variant(self, _len: usize, visitor: V) -> Result diff --git a/src/util.rs b/src/util.rs index e8bf354..bb62749 100644 --- a/src/util.rs +++ b/src/util.rs @@ -59,18 +59,12 @@ pub fn hex_dump(data: &[u8]) -> String { hex_line.clear(); char_line.clear(); } - let hex_str = match byte { - None => { " ".to_string() } - Some(b) => { format!("{b:02x} ") } - }; - let c_str = match byte { - None => { " ".to_string() } - Some(b) => { - let c = b as char; - let c = if c >= ' ' && c < (127 as char) { c } else { '.' }; - c.to_string() - } - }; + let hex_str = byte.map_or_else(|| " ".to_string(), |b| format!("{b:02x} ")); + let c_str = byte.map_or_else(|| " ".to_string(), |b| { + let c = b as char; + let c = if c >= ' ' && c < (127 as char) { c } else { '.' }; + c.to_string() + }); hex_line += &hex_str; char_line += &c_str; } diff --git a/tests/test_cp2cp.rs b/tests/test_cp2cp.rs index a48aff4..c5dd146 100644 --- a/tests/test_cp2cp.rs +++ b/tests/test_cp2cp.rs @@ -98,9 +98,8 @@ mod test { thread::spawn(move || { stdin.write_all(&block).expect("Failed to write to stdin"); }); - child.wait_with_output().map_err(|err| err.to_string()).and_then(|output| { - RpcValue::from_chainpack(output.stdout).map_err(|err| err.to_string()) - }) + let output = child.wait_with_output().map_err(|err| err.to_string())?; + RpcValue::from_chainpack(output.stdout).map_err(|err| err.to_string()) } #[test] From f1a1a4a45b82563f2ed8d7422e1e127f124f40a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 23:11:08 +0100 Subject: [PATCH 30/37] Ignore warning --- src/rpcvalue.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rpcvalue.rs b/src/rpcvalue.rs index e2693cd..adb79a8 100644 --- a/src/rpcvalue.rs +++ b/src/rpcvalue.rs @@ -57,6 +57,7 @@ pub enum Value { Bool(bool), DateTime(datetime::DateTime), Decimal(decimal::Decimal), + #[expect(clippy::box_collection, reason = "We're using the Box to lower stack size")] String(Box), Blob(Box), List(Box), From 02657e1b48acaccf246ccd2789da9d91dc9fc9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 23:13:23 +0100 Subject: [PATCH 31/37] decimal/datetime: Use self by value These types are Copy, so it makes sense to pass by value. --- src/datetime.rs | 24 ++++++++++++------------ src/decimal.rs | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/datetime.rs b/src/datetime.rs index 36ad9bc..6713207 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -118,7 +118,7 @@ impl DateTime { } Err(format!("Invalid DateTime: '{iso_str:?}")) } - pub fn epoc_msec_utc_offset(&self) -> (i64, i32) { + pub fn epoc_msec_utc_offset(self) -> (i64, i32) { let msec= self.0 / (TZ_MASK + 1); let mut offset = self.0 & TZ_MASK; if (offset & ((TZ_MASK + 1) / 2)) != 0 { @@ -129,22 +129,22 @@ impl DateTime { let offset = (offset * 15 * 60) as i32; (msec, offset) } - pub fn epoch_msec(&self) -> i64 { self.epoc_msec_utc_offset().0 } - pub fn utc_offset(&self) -> i32 { self.epoc_msec_utc_offset().1 } + pub fn epoch_msec(self) -> i64 { self.epoc_msec_utc_offset().0 } + pub fn utc_offset(self) -> i32 { self.epoc_msec_utc_offset().1 } - pub fn to_chrono_naivedatetime(&self) -> chrono::NaiveDateTime { + pub fn to_chrono_naivedatetime(self) -> chrono::NaiveDateTime { let msec = self.epoch_msec(); chrono::DateTime::from_timestamp_millis(msec).unwrap_or_default().naive_utc() } - pub fn to_chrono_datetime(&self) -> chrono::DateTime { + pub fn to_chrono_datetime(self) -> chrono::DateTime { let offset = FixedOffset::east_opt(self.utc_offset()) .unwrap_or_else(|| FixedOffset::east_opt(0).expect("Zero is within the range")); chrono::DateTime::from_naive_utc_and_offset(self.to_chrono_naivedatetime(), offset) } - pub fn to_iso_string(&self) -> String { + pub fn to_iso_string(self) -> String { self.to_iso_string_opt(&ToISOStringOptions::default()) } - pub fn to_iso_string_opt(&self, opts: &ToISOStringOptions) -> String { + pub fn to_iso_string_opt(self, opts: &ToISOStringOptions) -> String { let dt = self.to_chrono_datetime(); let mut s = format!("{}", dt.format("%Y-%m-%dT%H:%M:%S")); let ms = self.epoch_msec() % 1000; @@ -181,27 +181,27 @@ impl DateTime { } #[must_use] - pub fn add_days(&self, days: i64) -> Self { + pub fn add_days(self, days: i64) -> Self { let (msec, offset) = self.epoc_msec_utc_offset(); Self::from_epoch_msec_tz(msec + (days * 24 * 60 * 60 * 1000), offset) } #[must_use] - pub fn add_hours(&self, hours: i64) -> Self { + pub fn add_hours(self, hours: i64) -> Self { let (msec, offset) = self.epoc_msec_utc_offset(); Self::from_epoch_msec_tz(msec + (hours * 60 * 60 * 1000), offset) } #[must_use] - pub fn add_minutes(&self, minutes: i64) -> Self { + pub fn add_minutes(self, minutes: i64) -> Self { let (msec, offset) = self.epoc_msec_utc_offset(); Self::from_epoch_msec_tz(msec + (minutes * 60 * 1000), offset) } #[must_use] - pub fn add_seconds(&self, seconds: i64) -> Self { + pub fn add_seconds(self, seconds: i64) -> Self { let (msec, offset) = self.epoc_msec_utc_offset(); Self::from_epoch_msec_tz(msec + (seconds * 1000), offset) } #[must_use] - pub fn add_millis(&self, millis: i64) -> Self { + pub fn add_millis(self, millis: i64) -> Self { let (msec, offset) = self.epoc_msec_utc_offset(); Self::from_epoch_msec_tz(msec + millis, offset) } diff --git a/src/decimal.rs b/src/decimal.rs index 92946c7..2915bc9 100644 --- a/src/decimal.rs +++ b/src/decimal.rs @@ -12,7 +12,7 @@ impl Decimal { Decimal(n) } #[must_use] - pub fn normalize(&self) -> Decimal { + pub fn normalize(self) -> Decimal { let (mut mantissa, mut exponent) = self.decode(); if mantissa == 0 { return Decimal::new(0, 0); @@ -26,18 +26,18 @@ impl Decimal { Decimal::new(mantissa, exponent) } - pub fn decode(&self) -> (i64, i8) { + pub fn decode(self) -> (i64, i8) { let m = self.0 >> 8; let e = (self.0 & 0xFF) as i8; (m, e) } - pub fn mantissa(&self) -> i64 { + pub fn mantissa(self) -> i64 { self.decode().0 } - pub fn exponent(&self) -> i8 { + pub fn exponent(self) -> i8 { self.decode().1 } - pub fn to_cpon_string(&self) -> String { + pub fn to_cpon_string(self) -> String { let mut neg = false; let (mut mantissa, exponent) = self.decode(); if mantissa < 0 { @@ -79,7 +79,7 @@ impl Decimal { } s } - pub fn to_f64(&self) -> f64 { + pub fn to_f64(self) -> f64 { let decoded = self.decode(); let mut d = decoded.0 as f64; let exp = decoded.1; @@ -156,17 +156,17 @@ mod tests { #[test] fn decimal_normalization_removes_trailing_zeros() { let d1 = Decimal::new(1000, -3); - let n1 = Decimal::normalize(&d1); + let n1 = Decimal::normalize(d1); assert_eq!(n1.mantissa(), 1); assert_eq!(n1.exponent(), 0); let d2 = Decimal::new(1200, -3); - let n2 = Decimal::normalize(&d2); + let n2 = Decimal::normalize(d2); assert_eq!(n2.mantissa(), 12); assert_eq!(n2.exponent(), -1); let d3 = Decimal::new(500, -1); - let n3 = Decimal::normalize(&d3); + let n3 = Decimal::normalize(d3); assert_eq!(n3.mantissa(), 5); assert_eq!(n3.exponent(), 1); } @@ -174,7 +174,7 @@ mod tests { #[test] fn decimal_normalization_zero() { let zero = Decimal::new(0, -10); - let n = Decimal::normalize(&zero); + let n = Decimal::normalize(zero); assert_eq!(n.mantissa(), 0); assert_eq!(n.exponent(), 0); } @@ -182,7 +182,7 @@ mod tests { #[test] fn decimal_normalization_negative() { let d = Decimal::new(-5000, -3); - let n = Decimal::normalize(&d); + let n = Decimal::normalize(d); assert_eq!(n.mantissa(), -5); assert_eq!(n.exponent(), 0); } @@ -192,7 +192,7 @@ mod tests { // This also tests conversion to f64 with a positive/negative/zero exponent. for exp in [-3, 0, 3] { let d = Decimal::new(1200, exp); - let n = Decimal::normalize(&d); + let n = Decimal::normalize(d); let diff = (d.to_f64() - n.to_f64()).abs(); assert!(diff < 1e-12); } From 1a5abf34fa9d5dfe42c2cc49ddabe76ca512834d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 23:44:06 +0100 Subject: [PATCH 32/37] Simplify abs --- src/chainpack.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/chainpack.rs b/src/chainpack.rs index de4baad..21dab0a 100644 --- a/src/chainpack.rs +++ b/src/chainpack.rs @@ -148,15 +148,8 @@ where Ok(self.byte_writer.count() - cnt) } fn write_int_data(&mut self, number: i64) -> WriteResult { - let mut num: u64; - let neg; - if number < 0 { - num = (-number).cast_unsigned(); - neg = true; - } else { - num = number.cast_unsigned(); - neg = false; - } + let neg = number < 0; + let mut num = number.abs().cast_unsigned(); let bitlen = Self::significant_bits_part_length(num) + 1; // add sign bit if neg { From 090130ccc0a5740be45c360e8eaf7fc4f34ee29c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 23:46:17 +0100 Subject: [PATCH 33/37] Don't use as_bytes on literals --- src/bin/cp2cp.rs | 2 +- src/cpon.rs | 10 +++++----- src/json.rs | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/bin/cp2cp.rs b/src/bin/cp2cp.rs index 67cc7d6..8b61132 100644 --- a/src/bin/cp2cp.rs +++ b/src/bin/cp2cp.rs @@ -200,7 +200,7 @@ fn main() { wr.set_no_oneliners(opts.no_oneliners); if let Some(s) = &opts.indent { if s == "\\t" { - wr.set_indent("\t".as_bytes()); + wr.set_indent(b"\t"); } else { wr.set_indent(s.as_bytes()); } diff --git a/src/cpon.rs b/src/cpon.rs index 11d32b4..aa18bf3 100644 --- a/src/cpon.rs +++ b/src/cpon.rs @@ -24,7 +24,7 @@ impl<'a, W> CponWriter<'a, W> pub fn new(write: &'a mut W) -> Self { CponWriter { byte_writer: ByteWriter::new(write), - indent: "".as_bytes().to_vec(), + indent: b"".to_vec(), no_oneliners: false, nest_count: 0, } @@ -196,7 +196,7 @@ impl<'a, W> CponWriter<'a, W> Ok(self.byte_writer.count() - cnt) } fn write_datetime(&mut self, dt: DateTime) -> WriteResult { - let cnt = self.write_bytes("d\"".as_bytes())?; + let cnt = self.write_bytes(b"d\"")?; let s = dt.to_iso_string_opt(&ToISOStringOptions { include_millis: IncludeMilliseconds::WhenNonZero, include_timezone: true @@ -314,11 +314,11 @@ impl Writer for CponWriter<'_, W> fn write_value(&mut self, val: &Value) -> WriteResult { let cnt: usize = self.byte_writer.count(); match val { - Value::Null => self.write_bytes("null".as_bytes()), + Value::Null => self.write_bytes(b"null"), Value::Bool(b) => if *b { - self.write_bytes("true".as_bytes()) + self.write_bytes(b"true") } else { - self.write_bytes("false".as_bytes()) + self.write_bytes(b"false") }, Value::Int(n) => self.write_int(*n), Value::UInt(n) => { diff --git a/src/json.rs b/src/json.rs index 5b3c0f2..29ea44c 100644 --- a/src/json.rs +++ b/src/json.rs @@ -202,11 +202,11 @@ where W: Write fn write_value(&mut self, val: &Value) -> WriteResult { let cnt: usize = self.byte_writer.count(); match val { - Value::Null => self.write_bytes("null".as_bytes()), + Value::Null => self.write_bytes(b"null"), Value::Bool(b) => if *b { - self.write_bytes("true".as_bytes()) + self.write_bytes(b"true") } else { - self.write_bytes("false".as_bytes()) + self.write_bytes(b"false") }, Value::Int(n) => self.write_int(*n), Value::UInt(n) => self.write_int(n.cast_signed()), From 62e2b7e8379f10a6f5823e35f8456b7249132c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Wed, 4 Feb 2026 00:02:50 +0100 Subject: [PATCH 34/37] Use matches! --- src/datetime.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datetime.rs b/src/datetime.rs index 6713207..78897a7 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -79,7 +79,7 @@ impl DateTime { let mut msec = 0; let mut offset = 0; let mut rest = &s[PATTERN.len()..]; - if let Some(b'.') = rest.as_bytes().first() { + if matches!(rest.as_bytes().first(), Some(b'.')) { rest = &rest[1..]; if rest.len() >= 3 { match rest[..3].parse::() { From ac1be9a28df3c8e343e76de6ef1acde4bcb5d989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Wed, 4 Feb 2026 00:05:58 +0100 Subject: [PATCH 35/37] Use std::iter::once --- tests/test.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 450ff8d..6507e44 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -83,8 +83,8 @@ mod test { "oneFieldStruct" => shvproto::make_map!("x" => 4565), "vecIntField" => vec![1_i32, 2_i32].into_iter().map(shvproto::RpcValue::from).collect::>(), "vecEmptyStructField" => vec![shvproto::make_map!(), shvproto::make_map!()].into_iter().map(shvproto::RpcValue::from).collect::>(), - "mapIntField" => [("aaa".to_string(), 111)].into_iter().collect::>(), - "imapField" => [(420, 111)].into_iter().collect::>(), + "mapIntField" => std::iter::once(("aaa".to_string(), 111)).collect::>(), + "imapField" => std::iter::once((420, 111)).collect::>(), "rpcValueField" => RpcValue::from(42), ).into(); @@ -176,7 +176,7 @@ mod test { test_case(AllVariants::Blob(vec![1, 2, 3])); test_case(AllVariants::List(vec![shvproto::RpcValue::from("some_value")])); test_case(AllVariants::Map(shvproto::make_map!("key" => 1234))); - test_case(AllVariants::IMap([(420, 111.into())].into_iter().collect::>())); + test_case(AllVariants::IMap(std::iter::once((420, 111.into())).collect::>())); test_case(EnumWithUserStruct::OneFieldStructVariant(OneFieldStruct{x: 123})); test_case(EnumWithUserStruct::IntVariant(123)); From f20376e800b619b994eaf4cd4636bb763424eaa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Wed, 4 Feb 2026 00:07:18 +0100 Subject: [PATCH 36/37] Remove unnecessary clone --- tests/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test.rs b/tests/test.rs index 6507e44..3ee2706 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -200,7 +200,7 @@ mod test { fn generic_struct() { let int_struct_in: GenericStruct:: = shvproto::make_map!("x" => 123, "y" => vec![123, 456], "z" => "some_string").try_into().expect("Failed to parse"); let int_struct_rpcvalue: shvproto::RpcValue = int_struct_in.clone().into(); - let int_struct_out: GenericStruct:: = int_struct_rpcvalue.clone().try_into().expect("Failed to parse"); + let int_struct_out: GenericStruct:: = int_struct_rpcvalue.try_into().expect("Failed to parse"); assert_eq!(int_struct_in, int_struct_out); } From 481163f544fee9f904a15e86daddbe3923789978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Tue, 3 Feb 2026 15:56:41 +0100 Subject: [PATCH 37/37] Bump version --- Cargo.toml | 2 +- libshvproto-macros/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9b0a240..5e71da1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ name = "shvproto" description = "Rust implementation of the SHV protocol" license = "MIT" repository = "https://github.com/silicon-heaven/libshvproto-rs" -version = "4.0.1" +version = "5.0.0" edition = "2024" [dependencies] diff --git a/libshvproto-macros/Cargo.toml b/libshvproto-macros/Cargo.toml index 8ba7c99..4632f47 100644 --- a/libshvproto-macros/Cargo.toml +++ b/libshvproto-macros/Cargo.toml @@ -3,7 +3,7 @@ name = "libshvproto-macros" description = "Derive macros for libshvproto" license = "MIT" repository = "https://github.com/silicon-heaven/libshvproto-rs" -version = "0.2.1" +version = "0.2.2" edition = "2021" [lib]