#![forbid(unsafe_code)]
use prost_types::FieldMask;
pub trait FieldMaskExt {
fn is_valid_path(path: &str) -> bool;
fn is_valid(&self) -> bool;
fn canonical(&self) -> FieldMask;
fn union(&self, other: &FieldMask) -> FieldMask;
fn intersection(&self, other: &FieldMask) -> FieldMask;
}
impl FieldMaskExt for FieldMask {
fn is_valid_path(path: &str) -> bool {
if path.is_empty() {
return false;
}
for component in path.split('.') {
if !is_valid_component(component) {
return false;
}
}
true
}
fn is_valid(&self) -> bool {
self.paths.iter().all(|p| Self::is_valid_path(p))
}
fn canonical(&self) -> FieldMask {
let mut sorted: Vec<String> = self.paths.clone();
sorted.sort();
sorted.dedup();
let mut kept: Vec<String> = Vec::with_capacity(sorted.len());
'outer: for candidate in sorted {
for root in &kept {
if is_prefix_of(root, &candidate) {
continue 'outer;
}
}
kept.push(candidate);
}
FieldMask { paths: kept }
}
fn union(&self, other: &FieldMask) -> FieldMask {
let mut combined = self.paths.clone();
combined.extend_from_slice(&other.paths);
FieldMask { paths: combined }.canonical()
}
fn intersection(&self, other: &FieldMask) -> FieldMask {
use std::collections::HashSet;
let other_set: HashSet<&str> = other.paths.iter().map(|s| s.as_str()).collect();
let paths: Vec<String> = self
.paths
.iter()
.filter(|p| other_set.contains(p.as_str()))
.cloned()
.collect();
FieldMask { paths }.canonical()
}
}
fn is_valid_component(component: &str) -> bool {
if component.is_empty() {
return false;
}
let mut chars = component.chars();
let first = match chars.next() {
Some(c) => c,
None => return false,
};
if !matches!(first, 'a'..='z' | '_') {
return false;
}
chars.all(|c| matches!(c, 'a'..='z' | '0'..='9' | '_'))
}
fn is_prefix_of(prefix: &str, path: &str) -> bool {
path.len() > prefix.len()
&& path.starts_with(prefix)
&& path.as_bytes().get(prefix.len()) == Some(&b'.')
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_paths() {
assert!(FieldMask::is_valid_path("a"));
assert!(FieldMask::is_valid_path("foo"));
assert!(FieldMask::is_valid_path("foo_bar"));
assert!(FieldMask::is_valid_path("_field"));
assert!(FieldMask::is_valid_path("a.b"));
assert!(FieldMask::is_valid_path("foo.bar.baz"));
assert!(FieldMask::is_valid_path("a.b2.c_3"));
}
#[test]
fn invalid_paths() {
assert!(!FieldMask::is_valid_path(""));
assert!(!FieldMask::is_valid_path(".a"));
assert!(!FieldMask::is_valid_path("a."));
assert!(!FieldMask::is_valid_path("a..b"));
assert!(!FieldMask::is_valid_path("fooBar"));
assert!(!FieldMask::is_valid_path("1foo"));
assert!(!FieldMask::is_valid_path("a.1b"));
assert!(!FieldMask::is_valid_path("Foo"));
}
#[test]
fn canonical_sorts_and_dedupes() {
let mask = FieldMask {
paths: vec![
"c".to_string(),
"a".to_string(),
"b".to_string(),
"a".to_string(),
],
};
let c = mask.canonical();
assert_eq!(c.paths, vec!["a", "b", "c"]);
}
#[test]
fn canonical_removes_subpaths() {
let mask = FieldMask {
paths: vec!["a.b".to_string(), "a".to_string(), "a.b.c".to_string()],
};
let c = mask.canonical();
assert_eq!(c.paths, vec!["a"]);
}
#[test]
fn canonical_does_not_conflate_prefix_sibling() {
let mask = FieldMask {
paths: vec!["ab".to_string(), "a".to_string(), "a.b".to_string()],
};
let c = mask.canonical();
assert_eq!(c.paths, vec!["a", "ab"]);
}
#[test]
fn canonical_idempotent() {
let mask = FieldMask {
paths: vec!["x.y".to_string(), "x".to_string()],
};
let c1 = mask.canonical();
let c2 = c1.canonical();
assert_eq!(c1.paths, c2.paths);
}
#[test]
fn union_canonicalised() {
let a = FieldMask {
paths: vec!["x".to_string(), "y".to_string()],
};
let b = FieldMask {
paths: vec!["y".to_string(), "z".to_string(), "x.foo".to_string()],
};
let u = a.union(&b);
assert_eq!(u.paths, vec!["x", "y", "z"]);
}
#[test]
fn intersection_exact_match() {
let a = FieldMask {
paths: vec!["x".to_string(), "y".to_string(), "z".to_string()],
};
let b = FieldMask {
paths: vec!["y".to_string(), "z".to_string(), "w".to_string()],
};
let i = a.intersection(&b);
assert_eq!(i.paths, vec!["y", "z"]);
}
#[test]
fn intersection_empty_result() {
let a = FieldMask {
paths: vec!["a".to_string()],
};
let b = FieldMask {
paths: vec!["b".to_string()],
};
assert!(a.intersection(&b).paths.is_empty());
}
#[test]
fn empty_mask_operations() {
let empty = FieldMask { paths: vec![] };
let other = FieldMask {
paths: vec!["foo".to_string()],
};
assert!(empty.canonical().paths.is_empty());
assert_eq!(empty.union(&other).paths, vec!["foo"]);
assert!(empty.intersection(&other).paths.is_empty());
}
}