use serde::{Deserialize, Serialize};
use std::borrow::Cow;
pub const MAX_CONSTRAINT_NODES: usize = 12;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ConstraintKind {
All,
Any,
Enumerate,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(untagged)]
pub enum ConstraintExpr<'a> {
All {
all: Vec<ConstraintNode<'a>>,
},
Any {
any: Vec<ConstraintNode<'a>>,
},
Enumerate {
enumerate: Vec<ConstraintNode<'a>>,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(untagged)]
pub enum ConstraintNode<'a> {
Type(Cow<'a, str>),
Expr(ConstraintExpr<'a>),
}
impl ConstraintExpr<'_> {
pub fn evaluate<F>(&self, has_type: &F) -> bool
where
F: Fn(&str) -> bool,
{
match self {
ConstraintExpr::All { all } => all.iter().all(|n| n.evaluate(has_type)),
ConstraintExpr::Any { any } => any.iter().any(|n| n.evaluate(has_type)),
ConstraintExpr::Enumerate { enumerate } => {
enumerate.iter().any(|n| n.evaluate(has_type))
}
}
}
#[must_use]
pub fn validate_max_depth(&self, max_depth: usize) -> bool {
fn validate_expr(expr: &ConstraintExpr<'_>, depth: usize, max_depth: usize) -> bool {
if depth > max_depth {
return false;
}
match expr {
ConstraintExpr::All { all } => {
all.iter().all(|n| validate_node(n, depth, max_depth))
}
ConstraintExpr::Any { any } => {
any.iter().all(|n| validate_node(n, depth, max_depth))
}
ConstraintExpr::Enumerate { enumerate } => {
enumerate.iter().all(|n| validate_node(n, depth, max_depth))
}
}
}
fn validate_node(node: &ConstraintNode<'_>, parent_depth: usize, max_depth: usize) -> bool {
match node {
ConstraintNode::Type(_) => true,
ConstraintNode::Expr(child) => validate_expr(child, parent_depth + 1, max_depth),
}
}
validate_expr(self, 1, max_depth)
}
#[must_use]
pub fn validate_max_nodes(&self, max_nodes: usize) -> bool {
fn count_expr(expr: &ConstraintExpr<'_>, count: &mut usize, max_nodes: usize) -> bool {
*count += 1;
if *count > max_nodes {
return false;
}
match expr {
ConstraintExpr::All { all } => {
for n in all {
if !count_node(n, count, max_nodes) {
return false;
}
}
true
}
ConstraintExpr::Any { any } => {
for n in any {
if !count_node(n, count, max_nodes) {
return false;
}
}
true
}
ConstraintExpr::Enumerate { enumerate } => {
for n in enumerate {
if !count_node(n, count, max_nodes) {
return false;
}
}
true
}
}
}
fn count_node(node: &ConstraintNode<'_>, count: &mut usize, max_nodes: usize) -> bool {
match node {
ConstraintNode::Type(_) => {
*count += 1;
*count <= max_nodes
}
ConstraintNode::Expr(child) => count_expr(child, count, max_nodes),
}
}
let mut count = 0;
count_expr(self, &mut count, max_nodes)
}
}
impl ConstraintNode<'_> {
fn evaluate<F>(&self, has_type: &F) -> bool
where
F: Fn(&str) -> bool,
{
match self {
ConstraintNode::Type(t) => has_type(t),
ConstraintNode::Expr(expr) => expr.evaluate(has_type),
}
}
}