Skip to main content

tokf_hook_types/
engine.rs

1use serde::{Deserialize, Serialize};
2
3/// Configuration for an external permission engine.
4#[derive(Debug, Clone, Deserialize, Serialize)]
5pub struct ExternalEngineConfig {
6    /// Path to the external engine binary (resolved via PATH if not absolute).
7    pub command: String,
8
9    /// Arguments passed to the engine. Use `{format}` as a placeholder for the
10    /// tool format (e.g. `["hook", "handle", "--mode", "{format}"]`).
11    /// The placeholder is replaced with the resolved format string before spawning.
12    #[serde(default, skip_serializing_if = "Vec::is_empty")]
13    pub args: Vec<String>,
14
15    /// Timeout in milliseconds. Default: 5000 (5 seconds).
16    #[serde(default = "default_timeout")]
17    pub timeout_ms: u64,
18
19    /// What to do when the engine fails (crash, timeout, bad output).
20    #[serde(default)]
21    pub on_error: ErrorFallback,
22
23    /// Override the default format strings used for `{format}` substitution.
24    /// Keys are the default names (`claude-code`, `gemini`, `cursor`);
25    /// values are the replacements the engine expects.
26    ///
27    /// Example: `{ "claude-code" = "claude", "gemini" = "google" }`
28    #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")]
29    pub format_map: std::collections::HashMap<String, String>,
30}
31
32/// Behaviour when the external engine fails.
33#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)]
34#[serde(rename_all = "lowercase")]
35pub enum ErrorFallback {
36    /// Fail closed — prompt user for permission (default).
37    #[default]
38    Ask,
39    /// Fail open — auto-allow.
40    Allow,
41    /// Fall back to built-in rule matching.
42    Builtin,
43}
44
45impl Default for ExternalEngineConfig {
46    fn default() -> Self {
47        Self {
48            command: String::new(),
49            args: Vec::new(),
50            timeout_ms: default_timeout(),
51            on_error: ErrorFallback::default(),
52            format_map: std::collections::HashMap::new(),
53        }
54    }
55}
56
57impl ExternalEngineConfig {
58    /// Resolve the format string for a given hook format,
59    /// applying `format_map` overrides if present.
60    pub fn resolve_format(&self, format: crate::HookFormat) -> String {
61        let default = format.as_str();
62        self.format_map
63            .get(default)
64            .cloned()
65            .unwrap_or_else(|| default.to_string())
66    }
67}
68
69pub const fn default_timeout() -> u64 {
70    5000
71}