#[derive(Debug, Clone)]
pub struct Field {
pub path: Vec<u32>,
pub existence_only: bool,
}
impl Field {
pub fn new(path: Vec<u32>) -> Self {
Self {
path,
existence_only: false,
}
}
pub fn existence_only(mut self) -> Self {
self.existence_only = true;
self
}
}
#[derive(Debug, Clone)]
pub struct FieldProjection {
fields: Option<Vec<Field>>,
}
impl Default for FieldProjection {
fn default() -> Self {
Self::all()
}
}
impl FieldProjection {
pub fn all() -> Self {
Self { fields: None }
}
pub fn new() -> Self {
Self {
fields: Some(Vec::new()),
}
}
pub fn add_field(mut self, field: Field) -> Self {
if let Some(ref mut fields) = self.fields {
fields.push(field);
} else {
self.fields = Some(vec![Field::new(Vec::new()), field]);
}
self
}
pub fn is_all(&self) -> bool {
match &self.fields {
None => true,
Some(fields) => fields
.iter()
.any(|f| f.path.is_empty() && !f.existence_only),
}
}
pub fn fields(&self) -> Option<&[Field]> {
self.fields.as_deref()
}
pub(crate) fn has_full_include(&self) -> bool {
match &self.fields {
None => true,
Some(fields) => fields.iter().any(|f| !f.existence_only),
}
}
pub(crate) fn mentions_field(&self, field_number: u32) -> bool {
match &self.fields {
None => true,
Some(fields) => fields
.iter()
.any(|f| f.path.is_empty() || f.path.contains(&field_number)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builders_and_accessors() {
assert!(FieldProjection::all().is_all());
assert!(FieldProjection::all().fields().is_none());
assert!(!FieldProjection::new().is_all());
assert_eq!(FieldProjection::new().fields().map(|f| f.len()), Some(0));
let proj = FieldProjection::new()
.add_field(Field::new(vec![1]))
.add_field(Field::new(vec![2, 3]).existence_only());
let fields = proj.fields().expect("explicit projection has fields");
assert_eq!(fields.len(), 2);
assert!(!proj.is_all());
}
#[test]
fn has_full_include_semantics() {
assert!(FieldProjection::all().has_full_include());
let full = FieldProjection::new().add_field(Field::new(vec![1, 2]));
assert!(full.has_full_include());
let eo = FieldProjection::new()
.add_field(Field::new(vec![1]).existence_only())
.add_field(Field::new(vec![2, 3]).existence_only());
assert!(!eo.has_full_include());
}
#[test]
fn all_add_field_still_includes_all() {
let proj = FieldProjection::all().add_field(Field::new(vec![1]));
assert!(
proj.is_all(),
"all().add_field(...) must still include all fields"
);
let fields = proj.fields().expect("explicit field list after add_field");
assert_eq!(fields.len(), 2);
assert!(fields[0].path.is_empty(), "the include-all member persists");
assert_eq!(fields[1].path, vec![1]);
assert!(proj.mentions_field(2));
assert!(proj.has_full_include());
let proj = FieldProjection::default().add_field(Field::new(vec![3, 4]));
assert!(proj.is_all());
}
#[test]
fn empty_path_field_means_include_all() {
let proj = FieldProjection::new().add_field(Field::new(vec![]));
assert!(proj.is_all());
let proj = FieldProjection::new().add_field(Field::new(vec![]).existence_only());
assert!(!proj.is_all());
}
#[test]
fn mentions_field_matches_any_depth() {
assert!(FieldProjection::all().mentions_field(7));
let proj = FieldProjection::new().add_field(Field::new(vec![1, 5]));
assert!(proj.mentions_field(1));
assert!(proj.mentions_field(5)); assert!(!proj.mentions_field(2));
let empty_path = FieldProjection::new().add_field(Field::new(vec![]));
assert!(empty_path.mentions_field(42));
}
}