use chrono::{DateTime, Utc};
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq)]
pub enum AttrValue {
Bool(bool),
BoolWithTimestamp {
value: bool,
timestamp: Option<DateTime<Utc>>,
},
Enum(String),
List(Vec<String>),
Ref(Option<Uuid>),
}
impl AttrValue {
pub fn bool_with_timestamp(value: bool) -> Self {
AttrValue::BoolWithTimestamp {
value,
timestamp: if value { Some(Utc::now()) } else { None },
}
}
pub fn is_truthy(&self) -> bool {
match self {
AttrValue::Bool(v) => *v,
AttrValue::BoolWithTimestamp { value, .. } => *value,
AttrValue::Enum(_) => true,
AttrValue::List(v) => !v.is_empty(),
AttrValue::Ref(v) => v.is_some(),
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
AttrValue::Bool(v) => Some(*v),
AttrValue::BoolWithTimestamp { value, .. } => Some(*value),
_ => None,
}
}
pub fn as_enum(&self) -> Option<&str> {
match self {
AttrValue::Enum(s) => Some(s),
_ => None,
}
}
pub fn as_list(&self) -> Option<&[String]> {
match self {
AttrValue::List(v) => Some(v),
_ => None,
}
}
pub fn as_ref(&self) -> Option<Option<Uuid>> {
match self {
AttrValue::Ref(v) => Some(*v),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AttrSideEffect {
None,
PropagateStatusUp,
ValidateTags(Vec<String>),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bool_with_timestamp_true_has_timestamp() {
let value = AttrValue::bool_with_timestamp(true);
match value {
AttrValue::BoolWithTimestamp { value, timestamp } => {
assert!(value);
assert!(timestamp.is_some());
}
_ => panic!("Expected BoolWithTimestamp"),
}
}
#[test]
fn bool_with_timestamp_false_has_no_timestamp() {
let value = AttrValue::bool_with_timestamp(false);
match value {
AttrValue::BoolWithTimestamp { value, timestamp } => {
assert!(!value);
assert!(timestamp.is_none());
}
_ => panic!("Expected BoolWithTimestamp"),
}
}
#[test]
fn is_truthy_for_bool() {
assert!(AttrValue::Bool(true).is_truthy());
assert!(!AttrValue::Bool(false).is_truthy());
}
#[test]
fn is_truthy_for_bool_with_timestamp() {
assert!(AttrValue::bool_with_timestamp(true).is_truthy());
assert!(!AttrValue::bool_with_timestamp(false).is_truthy());
}
#[test]
fn is_truthy_for_list() {
assert!(AttrValue::List(vec!["a".into()]).is_truthy());
assert!(!AttrValue::List(vec![]).is_truthy());
}
#[test]
fn is_truthy_for_ref() {
assert!(AttrValue::Ref(Some(Uuid::new_v4())).is_truthy());
assert!(!AttrValue::Ref(None).is_truthy());
}
#[test]
fn is_truthy_for_enum() {
assert!(AttrValue::Enum("Done".into()).is_truthy());
assert!(AttrValue::Enum("".into()).is_truthy());
}
#[test]
fn as_bool_extracts_boolean() {
assert_eq!(AttrValue::Bool(true).as_bool(), Some(true));
assert_eq!(AttrValue::Bool(false).as_bool(), Some(false));
assert_eq!(AttrValue::bool_with_timestamp(true).as_bool(), Some(true));
assert_eq!(AttrValue::Enum("x".into()).as_bool(), None);
}
#[test]
fn as_enum_extracts_string() {
assert_eq!(AttrValue::Enum("Done".into()).as_enum(), Some("Done"));
assert_eq!(AttrValue::Bool(true).as_enum(), None);
}
#[test]
fn as_list_extracts_vec() {
let list = vec!["a".to_string(), "b".to_string()];
assert_eq!(
AttrValue::List(list.clone()).as_list(),
Some(list.as_slice())
);
assert_eq!(AttrValue::Bool(true).as_list(), None);
}
#[test]
fn as_ref_extracts_uuid() {
let id = Uuid::new_v4();
assert_eq!(AttrValue::Ref(Some(id)).as_ref(), Some(Some(id)));
assert_eq!(AttrValue::Ref(None).as_ref(), Some(None));
assert_eq!(AttrValue::Bool(true).as_ref(), None);
}
}