use std::collections::HashMap;
use super::classify::Effect;
use super::wire::Verdict;
use crate::opslog::{OpEntry, YOG_CONTROL};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Ruling {
Pass,
Hold,
Refuse,
}
impl Ruling {
pub(crate) fn word(self) -> &'static str {
match self {
Ruling::Pass => "pass",
Ruling::Hold => "hold",
Ruling::Refuse => "refuse",
}
}
pub fn of(word: &str) -> Option<Ruling> {
[Ruling::Pass, Ruling::Hold, Ruling::Refuse]
.into_iter()
.find(|r| r.word() == word)
}
pub fn verdict(self, why: &str) -> Verdict {
match self {
Ruling::Pass => Verdict::Pass,
Ruling::Hold => Verdict::Hold(why.to_owned()),
Ruling::Refuse => Verdict::Refuse(why.to_owned()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Table;
impl Table {
pub fn ruling(effect: Effect) -> Ruling {
match effect {
Effect::Read | Effect::TargetWrite | Effect::Process | Effect::OpenWorld => {
Ruling::Pass
}
Effect::Destructive | Effect::Secret => Ruling::Refuse,
}
}
}
const ANSWER: &str = "answer";
const FLOOR: &str = "floor";
const RAISE: &str = "raise";
const LOWER: &str = "lower";
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Answers {
once: HashMap<String, Ruling>,
floors: HashMap<String, bool>,
}
impl Answers {
pub fn fold(entries: &[OpEntry]) -> Answers {
let mut answers = Answers::default();
for argv in entries.iter().map(|e| &e.argv) {
let words: Vec<&str> = argv.iter().map(String::as_str).collect();
match words.as_slice() {
[YOG_CONTROL, ANSWER, key, word] => {
if let Some(ruling) = Ruling::of(word) {
answers.once.insert((*key).to_owned(), ruling);
}
}
[YOG_CONTROL, FLOOR, conv, state @ (RAISE | LOWER)] => {
answers.floors.insert((*conv).to_owned(), *state == RAISE);
}
_ => {}
}
}
answers
}
pub fn floored(&self, agent_id: &str) -> bool {
self.floors.iter().any(|(conv, raised)| {
*raised
&& (agent_id == conv
|| agent_id
.strip_prefix(conv.as_str())
.is_some_and(|rest| rest.starts_with('-')))
})
}
pub fn ruling(
&self,
tool_use_id: &str,
agent_id: &str,
effect: Effect,
policy: &super::policy::Policy,
) -> Ruling {
if let Some(once) = self.once.get(tool_use_id) {
return *once;
}
let table = policy.ruling(effect);
if effect > Effect::Read && self.floored(agent_id) {
return table.max(Ruling::Hold);
}
table
}
}
#[cfg(test)]
mod tests;