use crate::{
parser::{
parse_falkor_enum, redis_value_as_typed_string, redis_value_as_typed_string_vec,
redis_value_as_vec, SchemaParsable,
},
EntityType, FalkorDBError, FalkorResult, GraphSchema,
};
#[derive(Copy, Clone, Debug, Eq, PartialEq, strum::EnumString, strum::Display)]
#[strum(serialize_all = "UPPERCASE")]
pub enum ConstraintType {
Unique,
Mandatory,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, strum::EnumString, strum::Display)]
pub enum ConstraintStatus {
#[strum(serialize = "OPERATIONAL")]
Active,
#[strum(serialize = "UNDER CONSTRUCTION")]
Pending,
Failed,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Constraint {
pub constraint_type: ConstraintType,
pub label: String,
pub properties: Vec<String>,
pub entity_type: EntityType,
pub status: ConstraintStatus,
}
impl SchemaParsable for Constraint {
#[cfg_attr(
feature = "tracing",
tracing::instrument(name = "Parse Constraint", skip_all, level = "info")
)]
fn parse(
value: redis::Value,
_: &mut GraphSchema,
) -> FalkorResult<Self> {
let [constraint_type_raw, label_raw, properties_raw, entity_type_raw, status_raw]: [redis::Value; 5] = redis_value_as_vec(value)
.and_then(|res| res.try_into()
.map_err(|_| FalkorDBError::ParsingArrayToStructElementCount("Expected exactly 5 elements in constraint object")))?;
Ok(Self {
constraint_type: parse_falkor_enum(constraint_type_raw)?,
label: redis_value_as_typed_string(label_raw)?,
properties: redis_value_as_typed_string_vec(properties_raw)?,
entity_type: parse_falkor_enum(entity_type_raw)?,
status: parse_falkor_enum(status_raw)?,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_constraint_type_unique() {
assert_eq!(ConstraintType::Unique.to_string(), "UNIQUE");
}
#[test]
fn test_constraint_type_mandatory() {
assert_eq!(ConstraintType::Mandatory.to_string(), "MANDATORY");
}
#[test]
fn test_constraint_type_from_string() {
use std::str::FromStr;
assert_eq!(
ConstraintType::from_str("UNIQUE").unwrap(),
ConstraintType::Unique
);
assert_eq!(
ConstraintType::from_str("MANDATORY").unwrap(),
ConstraintType::Mandatory
);
}
#[test]
fn test_constraint_type_clone() {
let ct = ConstraintType::Unique;
let ct_clone = ct;
assert_eq!(ct, ct_clone);
}
#[test]
fn test_constraint_type_debug() {
assert!(format!("{:?}", ConstraintType::Unique).contains("Unique"));
assert!(format!("{:?}", ConstraintType::Mandatory).contains("Mandatory"));
}
#[test]
fn test_constraint_status_active() {
assert_eq!(ConstraintStatus::Active.to_string(), "OPERATIONAL");
}
#[test]
fn test_constraint_status_pending() {
assert_eq!(ConstraintStatus::Pending.to_string(), "UNDER CONSTRUCTION");
}
#[test]
fn test_constraint_status_failed() {
assert_eq!(ConstraintStatus::Failed.to_string(), "Failed");
}
#[test]
fn test_constraint_status_clone() {
let status = ConstraintStatus::Active;
let status_clone = status;
assert_eq!(status, status_clone);
}
#[test]
fn test_constraint_status_debug() {
assert!(format!("{:?}", ConstraintStatus::Active).contains("Active"));
assert!(format!("{:?}", ConstraintStatus::Pending).contains("Pending"));
assert!(format!("{:?}", ConstraintStatus::Failed).contains("Failed"));
}
#[test]
fn test_constraint_clone() {
let constraint = Constraint {
constraint_type: ConstraintType::Unique,
label: "Person".to_string(),
properties: vec!["id".to_string()],
entity_type: EntityType::Node,
status: ConstraintStatus::Active,
};
let constraint_clone = constraint.clone();
assert_eq!(constraint, constraint_clone);
}
#[test]
fn test_constraint_debug() {
let constraint = Constraint {
constraint_type: ConstraintType::Mandatory,
label: "User".to_string(),
properties: vec!["email".to_string()],
entity_type: EntityType::Node,
status: ConstraintStatus::Pending,
};
let debug_str = format!("{:?}", constraint);
assert!(debug_str.contains("Mandatory"));
assert!(debug_str.contains("User"));
assert!(debug_str.contains("email"));
}
}