#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttributeKind {
Bool,
BoolWithTimestamp,
Enum,
List,
Ref,
}
#[derive(Debug, Clone)]
pub struct AttributeSpec {
pub name: &'static str,
pub kind: AttributeKind,
pub filterable: bool,
pub propagates_up: bool,
pub cascades_on_delete: bool,
pub has_coupling: bool,
}
impl AttributeSpec {
const fn new(name: &'static str, kind: AttributeKind) -> Self {
Self {
name,
kind,
filterable: false,
propagates_up: false,
cascades_on_delete: false,
has_coupling: false,
}
}
const fn filterable(mut self) -> Self {
self.filterable = true;
self
}
const fn propagates_up(mut self) -> Self {
self.propagates_up = true;
self
}
const fn cascades_on_delete(mut self) -> Self {
self.cascades_on_delete = true;
self
}
const fn has_coupling(mut self) -> Self {
self.has_coupling = true;
self
}
}
pub const ATTRIBUTES: &[AttributeSpec] = &[
AttributeSpec::new("pinned", AttributeKind::BoolWithTimestamp)
.filterable()
.has_coupling(), AttributeSpec::new("protected", AttributeKind::Bool),
AttributeSpec::new("status", AttributeKind::Enum)
.filterable()
.propagates_up(),
AttributeSpec::new("tags", AttributeKind::List)
.filterable()
.cascades_on_delete(),
AttributeSpec::new("parent", AttributeKind::Ref),
];
pub fn get_spec(name: &str) -> Option<&'static AttributeSpec> {
ATTRIBUTES.iter().find(|spec| spec.name == name)
}
pub fn filterable_attrs() -> impl Iterator<Item = &'static str> {
ATTRIBUTES
.iter()
.filter(|spec| spec.filterable)
.map(|spec| spec.name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn attributes_registry_has_expected_entries() {
assert!(get_spec("pinned").is_some());
assert!(get_spec("protected").is_some());
assert!(get_spec("status").is_some());
assert!(get_spec("tags").is_some());
assert!(get_spec("parent").is_some());
}
#[test]
fn unknown_attribute_returns_none() {
assert!(get_spec("nonexistent").is_none());
}
#[test]
fn pinned_spec_is_correct() {
let spec = get_spec("pinned").unwrap();
assert_eq!(spec.name, "pinned");
assert_eq!(spec.kind, AttributeKind::BoolWithTimestamp);
assert!(spec.filterable);
assert!(spec.has_coupling);
assert!(!spec.propagates_up);
assert!(!spec.cascades_on_delete);
}
#[test]
fn status_spec_is_correct() {
let spec = get_spec("status").unwrap();
assert_eq!(spec.name, "status");
assert_eq!(spec.kind, AttributeKind::Enum);
assert!(spec.filterable);
assert!(spec.propagates_up);
}
#[test]
fn tags_spec_is_correct() {
let spec = get_spec("tags").unwrap();
assert_eq!(spec.name, "tags");
assert_eq!(spec.kind, AttributeKind::List);
assert!(spec.filterable);
assert!(spec.cascades_on_delete);
}
#[test]
fn protected_spec_is_correct() {
let spec = get_spec("protected").unwrap();
assert_eq!(spec.name, "protected");
assert_eq!(spec.kind, AttributeKind::Bool);
assert!(!spec.filterable); }
#[test]
fn parent_spec_is_correct() {
let spec = get_spec("parent").unwrap();
assert_eq!(spec.name, "parent");
assert_eq!(spec.kind, AttributeKind::Ref);
assert!(!spec.filterable); }
#[test]
fn filterable_attrs_returns_expected() {
let filterable: Vec<_> = filterable_attrs().collect();
assert!(filterable.contains(&"pinned"));
assert!(filterable.contains(&"status"));
assert!(filterable.contains(&"tags"));
assert!(!filterable.contains(&"protected"));
assert!(!filterable.contains(&"parent"));
}
}