This commit is contained in:
2023-09-08 17:25:36 +01:00
parent 08bc4cafa9
commit 3e0fae2c33
15 changed files with 1156 additions and 148 deletions

View File

@ -1,8 +1,8 @@
use itertools::Itertools;
use crate::FlatReqFilter;
use itertools::Itertools;
pub fn diff_filter(prev: &Vec<FlatReqFilter>, next: &Vec<FlatReqFilter>) -> Vec::<FlatReqFilter> {
let mut added: Vec::<FlatReqFilter> = vec![];
pub fn diff_filter(prev: &Vec<FlatReqFilter>, next: &Vec<FlatReqFilter>) -> Vec<FlatReqFilter> {
let mut added: Vec<FlatReqFilter> = vec![];
for n in next.iter() {
if !prev.iter().contains(&n) {
@ -19,38 +19,34 @@ mod tests {
#[test]
fn simple_diff_same() {
let prev = vec![
FlatReqFilter {
id: Some("a".to_owned()),
author: None,
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
}
];
let next = vec![
FlatReqFilter {
id: Some("a".to_owned()),
author: None,
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
}
];
let prev = vec![FlatReqFilter {
id: Some("a".to_owned()),
author: None,
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
}];
let next = vec![FlatReqFilter {
id: Some("a".to_owned()),
author: None,
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
}];
let result = diff_filter(&prev, &next);
assert_eq!(result, vec![])
@ -58,22 +54,20 @@ mod tests {
#[test]
fn simple_diff_add() {
let prev = vec![
FlatReqFilter {
id: Some("a".to_owned()),
author: None,
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
}
];
let prev = vec![FlatReqFilter {
id: Some("a".to_owned()),
author: None,
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
}];
let next = vec![
FlatReqFilter {
id: Some("a".to_owned()),
@ -106,8 +100,9 @@ mod tests {
];
let result = diff_filter(&prev, &next);
assert_eq!(result, vec![
FlatReqFilter {
assert_eq!(
result,
vec![FlatReqFilter {
id: Some("b".to_owned()),
author: None,
kind: None,
@ -120,48 +115,45 @@ mod tests {
since: None,
until: None,
limit: None,
}
])
}]
)
}
#[test]
fn simple_diff_replace() {
let prev = vec![
FlatReqFilter {
id: Some("a".to_owned()),
author: None,
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
}
];
let next = vec![
FlatReqFilter {
id: Some("b".to_owned()),
author: None,
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
];
let prev = vec![FlatReqFilter {
id: Some("a".to_owned()),
author: None,
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
}];
let next = vec![FlatReqFilter {
id: Some("b".to_owned()),
author: None,
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
}];
let result = diff_filter(&prev, &next);
assert_eq!(result, vec![
FlatReqFilter {
assert_eq!(
result,
vec![FlatReqFilter {
id: Some("b".to_owned()),
author: None,
kind: None,
@ -174,7 +166,7 @@ mod tests {
since: None,
until: None,
limit: None,
}
])
}]
)
}
}
}

View File

@ -1,5 +1,5 @@
use itertools::Itertools;
use crate::{FlatReqFilter, ReqFilter};
use itertools::Itertools;
#[derive(Clone)]
enum StringOrNumberEntry<'a> {
@ -12,39 +12,66 @@ pub fn expand_filter(filter: &ReqFilter) -> Vec<FlatReqFilter> {
let mut inputs: Vec<Vec<StringOrNumberEntry>> = vec![];
if let Some(ids) = &filter.ids {
let t_ids = ids.iter().map(|z| StringOrNumberEntry::String(("id", z))).collect_vec();
let t_ids = ids
.iter()
.map(|z| StringOrNumberEntry::String(("id", z)))
.collect_vec();
inputs.push(t_ids);
}
if let Some(authors) = &filter.authors {
let t_ids = authors.iter().map(|z| StringOrNumberEntry::String(("author", z))).collect_vec();
let t_ids = authors
.iter()
.map(|z| StringOrNumberEntry::String(("author", z)))
.collect_vec();
inputs.push(t_ids);
}
if let Some(kinds) = &filter.kinds {
let t_ids = kinds.iter().map(|z| StringOrNumberEntry::Number(("kind", z))).collect_vec();
let t_ids = kinds
.iter()
.map(|z| StringOrNumberEntry::Number(("kind", z)))
.collect_vec();
inputs.push(t_ids);
}
if let Some(e_tags) = &filter.e_tag {
let t_ids = e_tags.iter().map(|z| StringOrNumberEntry::String(("e_tag", z))).collect_vec();
let t_ids = e_tags
.iter()
.map(|z| StringOrNumberEntry::String(("e_tag", z)))
.collect_vec();
inputs.push(t_ids);
}
if let Some(p_tags) = &filter.p_tag {
let t_ids = p_tags.iter().map(|z| StringOrNumberEntry::String(("p_tag", z))).collect_vec();
let t_ids = p_tags
.iter()
.map(|z| StringOrNumberEntry::String(("p_tag", z)))
.collect_vec();
inputs.push(t_ids);
}
if let Some(d_tags) = &filter.d_tag {
let t_ids = d_tags.iter().map(|z| StringOrNumberEntry::String(("d_tag", z))).collect_vec();
let t_ids = d_tags
.iter()
.map(|z| StringOrNumberEntry::String(("d_tag", z)))
.collect_vec();
inputs.push(t_ids);
}
if let Some(t_tags) = &filter.t_tag {
let t_ids = t_tags.iter().map(|z| StringOrNumberEntry::String(("t_tag", z))).collect_vec();
let t_ids = t_tags
.iter()
.map(|z| StringOrNumberEntry::String(("t_tag", z)))
.collect_vec();
inputs.push(t_ids);
}
if let Some(r_tags) = &filter.r_tag {
let t_ids = r_tags.iter().map(|z| StringOrNumberEntry::String(("r_tag", z))).collect_vec();
let t_ids = r_tags
.iter()
.map(|z| StringOrNumberEntry::String(("r_tag", z)))
.collect_vec();
inputs.push(t_ids);
}
if let Some(search) = &filter.search {
let t_ids = search.iter().map(|z| StringOrNumberEntry::String(("search", z))).collect_vec();
let t_ids = search
.iter()
.map(|z| StringOrNumberEntry::String(("search", z)))
.collect_vec();
inputs.push(t_ids);
}
@ -155,28 +182,260 @@ mod tests {
let output = expand_filter(&input);
output.iter().take(5).for_each(|x| println!("{:?}", x));
let expected = vec![
FlatReqFilter { author: Some("a".to_owned()), kind: Some(1), id: Some("x".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("a".to_owned()), kind: Some(1), id: Some("y".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("a".to_owned()), kind: Some(2), id: Some("x".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("a".to_owned()), kind: Some(2), id: Some("y".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("a".to_owned()), kind: Some(3), id: Some("x".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("a".to_owned()), kind: Some(3), id: Some("y".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("b".to_owned()), kind: Some(1), id: Some("x".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("b".to_owned()), kind: Some(1), id: Some("y".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("b".to_owned()), kind: Some(2), id: Some("x".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("b".to_owned()), kind: Some(2), id: Some("y".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("b".to_owned()), kind: Some(3), id: Some("x".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("b".to_owned()), kind: Some(3), id: Some("y".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("c".to_owned()), kind: Some(1), id: Some("x".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("c".to_owned()), kind: Some(1), id: Some("y".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("c".to_owned()), kind: Some(2), id: Some("x".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("c".to_owned()), kind: Some(2), id: Some("y".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("c".to_owned()), kind: Some(3), id: Some("x".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter { author: Some("c".to_owned()), kind: Some(3), id: Some("y".to_owned()), p_tag: Some("a".to_owned()), t_tag: None, d_tag: None, r_tag: None, search: None, since: Some(99), until: None, limit: Some(10), e_tag: None },
FlatReqFilter {
author: Some("a".to_owned()),
kind: Some(1),
id: Some("x".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("a".to_owned()),
kind: Some(1),
id: Some("y".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("a".to_owned()),
kind: Some(2),
id: Some("x".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("a".to_owned()),
kind: Some(2),
id: Some("y".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("a".to_owned()),
kind: Some(3),
id: Some("x".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("a".to_owned()),
kind: Some(3),
id: Some("y".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("b".to_owned()),
kind: Some(1),
id: Some("x".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("b".to_owned()),
kind: Some(1),
id: Some("y".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("b".to_owned()),
kind: Some(2),
id: Some("x".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("b".to_owned()),
kind: Some(2),
id: Some("y".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("b".to_owned()),
kind: Some(3),
id: Some("x".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("b".to_owned()),
kind: Some(3),
id: Some("y".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("c".to_owned()),
kind: Some(1),
id: Some("x".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("c".to_owned()),
kind: Some(1),
id: Some("y".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("c".to_owned()),
kind: Some(2),
id: Some("x".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("c".to_owned()),
kind: Some(2),
id: Some("y".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("c".to_owned()),
kind: Some(3),
id: Some("x".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
FlatReqFilter {
author: Some("c".to_owned()),
kind: Some(3),
id: Some("y".to_owned()),
p_tag: Some("a".to_owned()),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: Some(99),
until: None,
limit: Some(10),
e_tag: None,
},
];
assert_eq!(output.len(), expected.len());
output
.iter()
.for_each(|a| assert!(expected.contains(a)));
output.iter().for_each(|a| assert!(expected.contains(a)));
}
}
}

View File

@ -1,12 +1,12 @@
extern crate wasm_bindgen;
use std::fmt::{Debug};
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;
mod expand;
mod diff;
mod expand;
mod merge;
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[derive(PartialEq, Clone, Serialize, Deserialize)]
pub struct ReqFilter {
#[serde(rename = "ids", skip_serializing_if = "Option::is_none")]
pub ids: Option<Vec<String>>,
@ -34,7 +34,13 @@ pub struct ReqFilter {
pub limit: Option<i32>,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
impl Debug for ReqFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&serde_json::to_string(self).unwrap().to_owned())
}
}
#[derive(PartialEq, Clone, Serialize, Deserialize)]
pub struct FlatReqFilter {
#[serde(rename = "ids", skip_serializing_if = "Option::is_none")]
id: Option<String>,
@ -62,6 +68,12 @@ pub struct FlatReqFilter {
limit: Option<i32>,
}
impl Debug for FlatReqFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&serde_json::to_string(self).unwrap().to_owned())
}
}
#[wasm_bindgen]
pub fn diff_filters(prev: JsValue, next: JsValue) -> Result<JsValue, JsValue> {
let prev_parsed: Vec<FlatReqFilter> = serde_wasm_bindgen::from_value(prev)?;
@ -75,4 +87,116 @@ pub fn expand_filter(val: JsValue) -> Result<JsValue, JsValue> {
let parsed: ReqFilter = serde_wasm_bindgen::from_value(val)?;
let result = expand::expand_filter(&parsed);
Ok(serde_wasm_bindgen::to_value(&result)?)
}
}
#[wasm_bindgen]
pub fn get_diff(prev: JsValue, next: JsValue) -> Result<JsValue, JsValue> {
let prev_parsed: Vec<ReqFilter> = serde_wasm_bindgen::from_value(prev)?;
let next_parsed: Vec<ReqFilter> = serde_wasm_bindgen::from_value(next)?;
let expanded_prev: Vec<FlatReqFilter> = prev_parsed
.iter()
.flat_map(|v| expand::expand_filter(v))
.collect();
let expanded_next: Vec<FlatReqFilter> = next_parsed
.iter()
.flat_map(|v| expand::expand_filter(v))
.collect();
let result = diff::diff_filter(&expanded_prev, &expanded_next);
Ok(serde_wasm_bindgen::to_value(&result)?)
}
#[wasm_bindgen]
pub fn flat_merge(val: JsValue) -> Result<JsValue, JsValue> {
let val_parsed: Vec<FlatReqFilter> = serde_wasm_bindgen::from_value(val)?;
let result = merge::flat_merge(&val_parsed);
Ok(serde_wasm_bindgen::to_value(&result)?)
}
#[cfg(test)]
mod tests {
use super::*;
use itertools::Itertools;
use std::cmp::Ordering;
#[test]
fn flat_merge_expanded() {
let input = vec![
ReqFilter {
ids: None,
kinds: Some(vec![1, 6969, 6]),
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
authors: Some(vec![
"kieran".to_string(),
"snort".to_string(),
"c".to_string(),
"d".to_string(),
"e".to_string(),
]),
since: Some(1),
until: Some(100),
search: None,
limit: None,
},
ReqFilter {
ids: None,
kinds: Some(vec![4]),
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
authors: Some(vec!["kieran".to_string()]),
limit: None,
},
ReqFilter {
ids: None,
authors: None,
kinds: Some(vec![4]),
e_tag: None,
p_tag: Some(vec!["kieran".to_string()]),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
ReqFilter {
ids: None,
kinds: Some(vec![1000]),
authors: Some(vec!["snort".to_string()]),
p_tag: Some(vec!["kieran".to_string()]),
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
e_tag: None,
limit: None,
},
];
let expanded = input
.iter()
.flat_map(|v| expand::expand_filter(v))
.sorted_by(|_, _| {
if rand::random() {
Ordering::Less
} else {
Ordering::Greater
}
})
.collect_vec();
let expanded_flat = merge::flat_merge(&expanded);
assert_eq!(expanded_flat, input);
}
}

View File

@ -0,0 +1,512 @@
use crate::{FlatReqFilter, ReqFilter};
use itertools::Itertools;
use std::cmp::Ordering;
pub fn flat_merge(all: &Vec<FlatReqFilter>) -> Vec<ReqFilter> {
let mut ret: Vec<ReqFilter> = vec![];
let merge_sets: Vec<Vec<&FlatReqFilter>> = vec![vec![all.first().unwrap()]];
let merge_sets = all
.iter()
.skip(1)
.sorted_by(|a, b| match distance(&a, &b) {
0 => Ordering::Equal,
1 => Ordering::Less,
_ => Ordering::Greater,
})
.fold(merge_sets, |mut acc, x| {
let mut did_match = false;
for y in acc.iter_mut() {
if y.iter().all(|z| can_merge_filters(z, x)) {
y.push(x);
did_match = true;
break;
}
}
if !did_match {
acc.push(vec![x]);
}
acc
});
for s in merge_sets.iter() {
ret.push(merge_set(s));
}
ret
}
fn merge_set(set: &Vec<&FlatReqFilter>) -> ReqFilter {
let ret = ReqFilter {
ids: None,
authors: None,
kinds: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
};
set.iter().fold(ret, |mut acc, x| {
array_prop_append(&x.id, &mut acc.ids);
array_prop_append(&x.author, &mut acc.authors);
array_prop_append(&x.kind, &mut acc.kinds);
array_prop_append(&x.e_tag, &mut acc.e_tag);
array_prop_append(&x.p_tag, &mut acc.p_tag);
array_prop_append(&x.t_tag, &mut acc.t_tag);
array_prop_append(&x.d_tag, &mut acc.d_tag);
array_prop_append(&x.r_tag, &mut acc.r_tag);
array_prop_append(&x.search, &mut acc.search);
acc.since = x.since;
acc.until = x.until;
acc.limit = x.limit;
acc
})
}
fn can_merge_filters(a: &FlatReqFilter, b: &FlatReqFilter) -> bool {
if a.since != b.since || a.until != b.until || a.limit != b.limit || a.search != b.search {
return false;
}
distance(a, b) <= 1
}
/// Calculate the distance in terms of similarity for merging
///
/// The goal of this function is to find 2 filters which are very similar where
/// one filter may have a single property change like so:
///
/// ```javascript
/// const a = { "kinds": 1, "authors": "a", "since": 99 };
/// const b = { "kinds": 1, "authors": "b", "since": 99 };
/// ```
/// In this case these 2 filters could be merged because their distance is `1`
/// ```javascript
/// const result = { "kinds": [1], "authors": ["a", "b"], "since": 99 };
/// ```
fn distance(a: &FlatReqFilter, b: &FlatReqFilter) -> u32 {
let mut ret = 0u32;
ret += prop_dist(&a.id, &b.id);
ret += prop_dist(&a.kind, &b.kind);
ret += prop_dist(&a.author, &b.author);
ret += prop_dist(&a.e_tag, &b.e_tag);
ret += prop_dist(&a.p_tag, &b.p_tag);
ret += prop_dist(&a.d_tag, &b.d_tag);
ret += prop_dist(&a.r_tag, &b.r_tag);
ret += prop_dist(&a.t_tag, &b.t_tag);
ret += prop_dist(&a.search, &b.search);
ret
}
#[inline(always)]
fn prop_dist<T: Eq>(a: &Option<T>, b: &Option<T>) -> u32 {
if (a.is_some() && b.is_none()) || (a.is_none() && b.is_some()) {
return 10;
} else if a.is_some() && a != b {
return 1;
}
0
}
#[inline(always)]
fn array_prop_append<T: Clone + Eq>(val: &Option<T>, arr: &mut Option<Vec<T>>) {
if let Some(ap) = val {
if arr.is_none() {
*arr = Some(vec![ap.clone()])
} else if !arr.as_ref().unwrap().contains(ap) {
arr.as_mut().unwrap().push(ap.clone());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn distance() {
let a = FlatReqFilter {
id: Some("a".to_owned()),
author: None,
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
};
let b = FlatReqFilter {
id: Some("a".to_owned()),
author: None,
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
};
let c = FlatReqFilter {
id: Some("c".to_owned()),
author: None,
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
};
let d = FlatReqFilter {
id: Some("a".to_owned()),
author: None,
kind: Some(1),
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
};
let e = FlatReqFilter {
id: Some("e".to_owned()),
author: None,
kind: Some(1),
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
};
assert_eq!(super::distance(&a, &b), 0);
assert_eq!(super::distance(&a, &c), 1);
assert_eq!(super::distance(&a, &d), 10);
assert_eq!(super::distance(&a, &e), 11);
}
#[test]
fn merge_set() {
let a = FlatReqFilter {
id: Some("0".to_owned()),
author: Some("a".to_owned()),
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: Some(10),
};
let b = FlatReqFilter {
id: Some("0".to_owned()),
author: Some("b".to_owned()),
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: Some(10),
};
let output = ReqFilter {
ids: Some(vec!["0".to_owned()]),
authors: Some(vec!["a".to_owned(), "b".to_owned()]),
kinds: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: Some(10),
};
assert_eq!(super::merge_set(&vec![&a, &b]), output);
}
#[test]
fn can_merge_filters() {
let a = FlatReqFilter {
id: Some("0".to_owned()),
author: Some("a".to_owned()),
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: Some(10),
};
let b = FlatReqFilter {
id: Some("0".to_owned()),
author: Some("b".to_owned()),
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: Some(10),
};
let c = FlatReqFilter {
id: Some("0".to_owned()),
author: Some("b".to_owned()),
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: Some(100),
};
assert!(super::can_merge_filters(&a, &b));
assert!(!super::can_merge_filters(&b, &c));
}
#[test]
fn flat_merge() {
let input = vec![
FlatReqFilter {
id: Some("0".to_owned()),
author: Some("a".to_owned()),
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
FlatReqFilter {
id: Some("0".to_owned()),
author: Some("b".to_owned()),
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
FlatReqFilter {
id: None,
author: None,
kind: Some(1),
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
FlatReqFilter {
id: None,
author: None,
kind: Some(2),
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
FlatReqFilter {
id: None,
author: None,
kind: Some(2),
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
FlatReqFilter {
id: Some("0".to_owned()),
author: Some("c".to_owned()),
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
FlatReqFilter {
id: None,
author: Some("c".to_owned()),
kind: Some(1),
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
FlatReqFilter {
id: None,
author: Some("c".to_owned()),
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: Some(100),
},
FlatReqFilter {
id: Some("1".to_owned()),
author: Some("c".to_owned()),
kind: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
];
let output = vec![
ReqFilter {
ids: Some(vec!["0".to_owned()]),
authors: Some(vec!["a".to_owned(), "b".to_owned(), "c".to_owned()]),
kinds: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
ReqFilter {
ids: None,
authors: None,
kinds: Some(vec![1, 2]),
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
ReqFilter {
ids: None,
authors: Some(vec!["c".to_owned()]),
kinds: Some(vec![1]),
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
ReqFilter {
ids: None,
authors: Some(vec!["c".to_owned()]),
kinds: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: Some(100),
},
ReqFilter {
ids: Some(vec!["1".to_owned()]),
authors: Some(vec!["c".to_owned()]),
kinds: None,
e_tag: None,
p_tag: None,
t_tag: None,
d_tag: None,
r_tag: None,
search: None,
since: None,
until: None,
limit: None,
},
];
assert_eq!(super::flat_merge(&input), output)
}
}