Skip to content

Commit ca1abfe

Browse files
committed
Support iterables in compare_fields
1 parent e2a60a6 commit ca1abfe

File tree

4 files changed

+40
-13
lines changed

4 files changed

+40
-13
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/compare_fields/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ version = "0.2.0"
44
authors = ["Paul Hauner <[email protected]>"]
55
edition = { workspace = true }
66

7+
[dependencies]
8+
itertools = { workspace = true }
9+
710
[dev-dependencies]
811
compare_fields_derive = { workspace = true }
912

common/compare_fields/src/lib.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,8 @@
8181
//! }
8282
//! ];
8383
//! assert_eq!(bar_a.compare_fields(&bar_b), bar_a_b);
84-
//!
85-
//!
86-
//!
87-
//! // TODO:
8884
//! ```
85+
use itertools::{EitherOrBoth, Itertools};
8986
use std::fmt::Debug;
9087

9188
#[derive(Debug, PartialEq, Clone)]
@@ -112,13 +109,38 @@ impl Comparison {
112109
}
113110

114111
pub fn from_slice<T: Debug + PartialEq<T>>(field_name: String, a: &[T], b: &[T]) -> Self {
112+
Self::from_iter(field_name, a.iter(), b.iter())
113+
}
114+
115+
pub fn from_into_iter<'a, T: Debug + PartialEq + 'a>(
116+
field_name: String,
117+
a: impl IntoIterator<Item = &'a T>,
118+
b: impl IntoIterator<Item = &'a T>,
119+
) -> Self {
120+
Self::from_iter(field_name, a.into_iter(), b.into_iter())
121+
}
122+
123+
pub fn from_iter<'a, T: Debug + PartialEq + 'a>(
124+
field_name: String,
125+
a: impl Iterator<Item = &'a T>,
126+
b: impl Iterator<Item = &'a T>,
127+
) -> Self {
115128
let mut children = vec![];
129+
let mut all_equal = true;
116130

117-
for i in 0..std::cmp::max(a.len(), b.len()) {
118-
children.push(FieldComparison::new(format!("{i}"), &a.get(i), &b.get(i)));
131+
for (i, entry) in a.zip_longest(b).enumerate() {
132+
let comparison = match entry {
133+
EitherOrBoth::Both(x, y) => {
134+
FieldComparison::new(format!("{i}"), &Some(x), &Some(y))
135+
}
136+
EitherOrBoth::Left(x) => FieldComparison::new(format!("{i}"), &Some(x), &None),
137+
EitherOrBoth::Right(y) => FieldComparison::new(format!("{i}"), &None, &Some(y)),
138+
};
139+
all_equal = all_equal && comparison.equal();
140+
children.push(comparison);
119141
}
120142

121-
Self::parent(field_name, a == b, children)
143+
Self::parent(field_name, all_equal, children)
122144
}
123145

124146
pub fn retain_children<F>(&mut self, f: F)

common/compare_fields_derive/src/lib.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ use proc_macro::TokenStream;
44
use quote::quote;
55
use syn::{parse_macro_input, DeriveInput};
66

7-
fn is_slice(field: &syn::Field) -> bool {
7+
fn is_iter(field: &syn::Field) -> bool {
88
field.attrs.iter().any(|attr| {
99
attr.path.is_ident("compare_fields")
10-
&& attr.tokens.to_string().replace(' ', "") == "(as_slice)"
10+
&& (attr.tokens.to_string().replace(' ', "") == "(as_slice)"
11+
|| attr.tokens.to_string().replace(' ', "") == "(as_iter)")
1112
})
1213
}
1314

@@ -34,13 +35,13 @@ pub fn compare_fields_derive(input: TokenStream) -> TokenStream {
3435
let field_name = ident_a.to_string();
3536
let ident_b = ident_a.clone();
3637

37-
let quote = if is_slice(field) {
38+
let quote = if is_iter(field) {
3839
quote! {
39-
comparisons.push(compare_fields::Comparison::from_slice(
40+
comparisons.push(compare_fields::Comparison::from_into_iter(
4041
#field_name.to_string(),
4142
&self.#ident_a,
42-
&b.#ident_b)
43-
);
43+
&b.#ident_b
44+
));
4445
}
4546
} else {
4647
quote! {

0 commit comments

Comments
 (0)