use crate::executor::{AGENT, HUMAN, RULE};
use serde_yaml::{Mapping, Value as Yaml};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuleKind {
Path,
Absent,
Contains,
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, expand: F) -> Criterion
where
F: Fn(&str) -> String,
{
let ex = |value: &str| {
if value.contains("{{") {
expand(value)
} else {
value.to_string()
}
};
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),
},
}
}
}
#[derive(Debug, Clone)]
pub struct RuleItem {
pub description: String,
pub kind: Option<RuleKind>,
pub args: Vec<String>,
}
impl RuleItem {
pub fn machine(&self) -> bool {
self.kind.is_some()
}
}
pub fn items_of(criteria: &[Criterion]) -> Vec<RuleItem> {
criteria
.iter()
.map(|criterion| {
let description = criterion.text();
let (kind, args) = match criterion {
Criterion::PathExists { path, .. } => (Some(RuleKind::Path), vec![path.clone()]),
Criterion::PathAbsent { absent, .. } => {
(Some(RuleKind::Absent), vec![absent.clone()])
}
Criterion::FileContains { file, contains, .. } => (
Some(RuleKind::Contains),
vec![file.clone(), contains.clone()],
),
Criterion::CommandRun { run, .. } => (Some(RuleKind::Run), vec![run.clone()]),
Criterion::AgentJudgement { .. } | Criterion::HumanGate { .. } => {
(None, Vec::new())
}
};
RuleItem {
description,
kind,
args,
}
})
.collect()
}