use crate::executor::{AGENT, HUMAN, RULE};
use crate::paths::replace_placeholders;
use serde_yaml::{Mapping, Value as Yaml};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuleKind {
PathExists,
PathAbsent,
FileContains,
CommandRun,
}
impl RuleKind {
pub fn as_str(&self) -> &'static str {
match self {
RuleKind::PathExists => "path",
RuleKind::PathAbsent => "absent",
RuleKind::FileContains => "contains",
RuleKind::CommandRun => "run",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Criterion {
PathExists {
path: String,
description: String,
},
PathAbsent {
absent: String,
description: String,
},
FileContains {
file: String,
contains: String,
description: String,
},
CommandRun {
run: String,
description: String,
},
AgentJudgement {
description: String,
},
HumanGate {
description: String,
},
}
impl Criterion {
pub fn executor(&self) -> &'static str {
match self {
Criterion::PathExists { .. }
| Criterion::PathAbsent { .. }
| Criterion::FileContains { .. }
| Criterion::CommandRun { .. } => RULE,
Criterion::AgentJudgement { .. } => AGENT,
Criterion::HumanGate { .. } => HUMAN,
}
}
pub fn description(&self) -> &str {
match self {
Criterion::PathExists { description, .. }
| Criterion::PathAbsent { description, .. }
| Criterion::FileContains { description, .. }
| Criterion::CommandRun { description, .. }
| Criterion::AgentJudgement { description }
| Criterion::HumanGate { description } => description,
}
}
pub fn text(&self) -> String {
if !self.description().is_empty() {
return self.description().to_string();
}
match self {
Criterion::PathExists { path, .. } => format!("存在:{path}"),
Criterion::PathAbsent { absent, .. } => format!("不存在:{absent}"),
Criterion::FileContains { file, contains, .. } => {
format!("含「{contains}」:{file}")
}
Criterion::CommandRun { run, .. } => format!("跑通:{run}"),
Criterion::AgentJudgement { .. } | Criterion::HumanGate { .. } => String::new(),
}
}
pub fn to_yaml(&self) -> Yaml {
let mut pairs: Vec<(&str, &str)> = Vec::new();
match self {
Criterion::PathExists { path, description } => {
pairs.push(("executor", RULE));
pairs.push(("path", path));
if !description.is_empty() {
pairs.push(("description", description));
}
}
Criterion::PathAbsent {
absent,
description,
} => {
pairs.push(("executor", RULE));
pairs.push(("absent", absent));
if !description.is_empty() {
pairs.push(("description", description));
}
}
Criterion::FileContains {
file,
contains,
description,
} => {
pairs.push(("executor", RULE));
pairs.push(("file", file));
pairs.push(("contains", contains));
if !description.is_empty() {
pairs.push(("description", description));
}
}
Criterion::CommandRun { run, description } => {
pairs.push(("executor", RULE));
pairs.push(("run", run));
if !description.is_empty() {
pairs.push(("description", description));
}
}
Criterion::AgentJudgement { description } => {
pairs.push(("executor", AGENT));
pairs.push(("description", description));
}
Criterion::HumanGate { description } => {
pairs.push(("executor", HUMAN));
pairs.push(("description", description));
}
}
let mut map = Mapping::new();
for (key, value) in pairs {
map.insert(
Yaml::String(key.to_string()),
Yaml::String(value.to_string()),
);
}
Yaml::Mapping(map)
}
pub fn expanded<F>(&self, resolve: F) -> Criterion
where
F: Fn(&str) -> Option<String>,
{
let ex = |value: &str| replace_placeholders(value, &resolve);
match self {
Criterion::PathExists { path, description } => Criterion::PathExists {
path: ex(path),
description: ex(description),
},
Criterion::PathAbsent {
absent,
description,
} => Criterion::PathAbsent {
absent: ex(absent),
description: ex(description),
},
Criterion::FileContains {
file,
contains,
description,
} => Criterion::FileContains {
file: ex(file),
contains: ex(contains),
description: ex(description),
},
Criterion::CommandRun { run, description } => Criterion::CommandRun {
run: ex(run),
description: ex(description),
},
Criterion::AgentJudgement { description } => Criterion::AgentJudgement {
description: ex(description),
},
Criterion::HumanGate { description } => Criterion::HumanGate {
description: ex(description),
},
}
}
}