use crate::state::CrdtState;
use crate::validator::{ProposedChange, ValidationOutcome, Validator};
#[derive(Debug)]
pub enum PreValidationResult {
Proceed,
FastReject { constraint: String, reason: String },
}
pub fn pre_validate(
validator: &Validator,
state: &CrdtState,
change: &ProposedChange,
) -> PreValidationResult {
match validator.validate(state, change) {
ValidationOutcome::Accepted => PreValidationResult::Proceed,
ValidationOutcome::Rejected(violations) => {
let v = &violations[0];
PreValidationResult::FastReject {
constraint: v.constraint_name.clone(),
reason: v.reason.clone(),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::constraint::ConstraintSet;
use loro::LoroValue;
#[test]
fn pre_validate_fast_rejects_not_null() {
let state = CrdtState::new(1).unwrap();
let mut cs = ConstraintSet::new();
cs.add_not_null("name_nn", "users", "name");
let validator = Validator::new(cs, 10);
let change = ProposedChange {
collection: "users".into(),
row_id: "u1".into(),
surrogate: nodedb_types::Surrogate::ZERO,
fields: vec![("email".into(), LoroValue::String("a@b.com".into()))],
};
match pre_validate(&validator, &state, &change) {
PreValidationResult::FastReject { constraint, .. } => {
assert_eq!(constraint, "name_nn");
}
_ => panic!("expected fast reject"),
}
}
#[test]
fn pre_validate_proceeds_when_valid() {
let state = CrdtState::new(1).unwrap();
let mut cs = ConstraintSet::new();
cs.add_not_null("name_nn", "users", "name");
let validator = Validator::new(cs, 10);
let change = ProposedChange {
collection: "users".into(),
row_id: "u1".into(),
surrogate: nodedb_types::Surrogate::ZERO,
fields: vec![("name".into(), LoroValue::String("Alice".into()))],
};
assert!(matches!(
pre_validate(&validator, &state, &change),
PreValidationResult::Proceed
));
}
}