Skip to main content

netget/tui/
driver.rs

1//! Who answers an instance's events, in one word.
2//!
3//! An instance's handler table can hold any number of rules, but the one
4//! that decides its character is the wildcard: what happens to an event no
5//! specific rule claims. That is the **driver**, and it is what the list badge
6//! shows and what `[ driver: … ]` / `m` cycles. Cycling rewrites only the
7//! wildcard rule; every specific rule survives.
8
9use serde_json::{json, Value};
10
11use crate::scripting::event_handler::EventPattern;
12use crate::scripting::{EventHandlerConfig, EventHandlerType};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Driver {
16    /// `*` → manual: every unmatched event parks for the human.
17    Manual,
18    /// No wildcard rule (or `*` → LLM): the model answers, from the instance
19    /// instruction.
20    Llm,
21    /// `*` → static with no actions: acknowledge, never reply.
22    Silent,
23    /// `*` → a script or a static list with actions: something custom.
24    Rules,
25}
26
27impl Driver {
28    /// Badge text.
29    pub fn label(&self) -> &'static str {
30        match self {
31            Driver::Manual => "MANUAL",
32            Driver::Llm => "LLM",
33            Driver::Silent => "SILENT",
34            Driver::Rules => "RULES",
35        }
36    }
37
38    /// One phrase for the overview.
39    pub fn describe(&self) -> &'static str {
40        match self {
41            Driver::Manual => "you answer each event here",
42            Driver::Llm => "the model answers, from the instruction",
43            Driver::Silent => "events are acknowledged and never answered",
44            Driver::Rules => "a wildcard rule answers (script or fixed actions)",
45        }
46    }
47
48    /// The driver `m` moves to. `Rules` is not a target — it is whatever the
49    /// user built — so from it the cycle restarts at MANUAL.
50    pub fn next(&self) -> Driver {
51        match self {
52            Driver::Manual => Driver::Llm,
53            Driver::Llm => Driver::Silent,
54            Driver::Silent | Driver::Rules => Driver::Manual,
55        }
56    }
57}
58
59/// Read the driver off a handler table.
60pub fn driver_of(routing: Option<&EventHandlerConfig>) -> Driver {
61    let Some(config) = routing else {
62        return Driver::Llm;
63    };
64    let wildcard = config
65        .handlers
66        .iter()
67        .find(|h| matches!(h.event_pattern, EventPattern::Wildcard));
68    match wildcard.map(|h| &h.handler) {
69        None => Driver::Llm,
70        Some(EventHandlerType::Manual { .. }) => Driver::Manual,
71        Some(EventHandlerType::Llm { .. }) => Driver::Llm,
72        Some(EventHandlerType::Static { actions }) if actions.is_empty() => Driver::Silent,
73        Some(EventHandlerType::Static { .. }) | Some(EventHandlerType::Script { .. }) => {
74            Driver::Rules
75        }
76    }
77}
78
79/// How many specific (non-wildcard) rules the table holds.
80pub fn specific_rule_count(routing: Option<&EventHandlerConfig>) -> usize {
81    routing
82        .map(|c| {
83            c.handlers
84                .iter()
85                .filter(|h| !matches!(h.event_pattern, EventPattern::Wildcard))
86                .count()
87        })
88        .unwrap_or(0)
89}
90
91/// Rebuild the table as JSON with the wildcard rule replaced for `driver`.
92///
93/// Specific rules are kept in order; the wildcard goes last, where
94/// first-match-wins semantics need it. `Llm` means *no* wildcard, which is
95/// how the instance instruction becomes the fallback. `Rules` cannot be
96/// expressed (it is not a single rule) and is treated as `Manual`.
97pub fn handlers_with_driver(routing: Option<&EventHandlerConfig>, driver: Driver) -> Vec<Value> {
98    let mut out: Vec<Value> = routing
99        .map(|c| {
100            c.handlers
101                .iter()
102                .filter(|h| !matches!(h.event_pattern, EventPattern::Wildcard))
103                .filter_map(|h| serde_json::to_value(h).ok())
104                .collect()
105        })
106        .unwrap_or_default();
107    match driver {
108        Driver::Llm => {}
109        Driver::Silent => out.push(json!({
110            "event_pattern": "*",
111            "handler": {"type": "static", "actions": []}
112        })),
113        Driver::Manual | Driver::Rules => out.push(json!({
114            "event_pattern": "*",
115            "handler": {"type": "manual"}
116        })),
117    }
118    out
119}