1use serde::{Deserialize, Serialize};
2
3#[cfg(feature = "local")]
4use crate::local::LocalBackend;
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub enum Capability {
9 ReadFile,
11 WriteFile,
13 GitReadOnly,
15 ShellCommand { pattern: String },
17 Network,
19 Custom(String),
21}
22
23#[derive(Debug, Clone, Default, Serialize, Deserialize)]
28pub struct Sandbox {
29 pub allowed: Vec<Capability>,
31 pub denied: Vec<Capability>,
33}
34
35#[derive(Debug, Clone, Default)]
37pub struct ProviderConfig {
38 #[cfg(feature = "local")]
40 pub backend: Option<LocalBackend>,
41 pub model: Option<String>,
43 pub budget: Option<f64>,
45 pub sandbox: Option<Sandbox>,
47 pub debug: bool,
49}
50
51pub(crate) const GIT_READONLY_COMMANDS: &[&str] = &[
53 "diff", "log", "show", "status", "ls-files", "rev-parse", "branch", "cat-file", "rev-list",
54 "shortlog", "blame",
55];
56
57pub(crate) trait SandboxTranslator {
59 fn translate_allowed(&self, capabilities: &[Capability]) -> Vec<String>;
61
62 fn translate_sandbox(&self, sandbox: &Sandbox) -> Vec<String> {
64 let allowed: Vec<_> = sandbox
65 .allowed
66 .iter()
67 .filter(|cap| !sandbox.denied.contains(cap))
68 .cloned()
69 .collect();
70 self.translate_allowed(&allowed)
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn sandbox_default_is_empty() {
80 let sandbox = Sandbox::default();
81 assert!(sandbox.allowed.is_empty());
82 assert!(sandbox.denied.is_empty());
83 }
84
85 #[test]
86 fn provider_config_default() {
87 let config = ProviderConfig::default();
88 assert!(config.model.is_none());
89 assert!(config.budget.is_none());
90 assert!(config.sandbox.is_none());
91 assert!(!config.debug);
92 }
93
94 #[test]
95 fn capability_equality() {
96 assert_eq!(Capability::ReadFile, Capability::ReadFile);
97 assert_ne!(Capability::ReadFile, Capability::WriteFile);
98 assert_eq!(
99 Capability::ShellCommand {
100 pattern: "ls".into()
101 },
102 Capability::ShellCommand {
103 pattern: "ls".into()
104 }
105 );
106 assert_ne!(
107 Capability::ShellCommand {
108 pattern: "ls".into()
109 },
110 Capability::ShellCommand {
111 pattern: "cat".into()
112 }
113 );
114 }
115}