1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
10#[serde(rename_all = "lowercase")]
11pub enum SandboxMode {
12 Off,
14 #[default]
16 Workspace,
17}
18
19impl std::str::FromStr for SandboxMode {
20 type Err = String;
21
22 fn from_str(s: &str) -> Result<Self, Self::Err> {
23 match s.to_ascii_lowercase().as_str() {
24 "off" | "none" | "false" | "0" => Ok(Self::Off),
25 "workspace" | "on" | "true" | "1" => Ok(Self::Workspace),
26 other => Err(format!(
27 "unknown sandbox mode '{other}' (expected off or workspace)"
28 )),
29 }
30 }
31}
32
33impl SandboxMode {
34 pub fn as_str(self) -> &'static str {
35 match self {
36 Self::Off => "off",
37 Self::Workspace => "workspace",
38 }
39 }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
44#[serde(rename_all = "lowercase")]
45pub enum SandboxFallback {
46 #[default]
48 Allow,
49 Deny,
51}
52
53impl std::str::FromStr for SandboxFallback {
54 type Err = String;
55
56 fn from_str(s: &str) -> Result<Self, Self::Err> {
57 match s.to_ascii_lowercase().as_str() {
58 "allow" | "warn" | "host" => Ok(Self::Allow),
59 "deny" | "error" | "strict" => Ok(Self::Deny),
60 other => Err(format!(
61 "unknown sandbox_fallback '{other}' (expected allow or deny)"
62 )),
63 }
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct SandboxSettings {
70 pub mode: SandboxMode,
71 pub network: bool,
72 pub fallback: SandboxFallback,
73}
74
75impl Default for SandboxSettings {
76 fn default() -> Self {
77 Self {
78 mode: SandboxMode::Workspace,
79 network: true,
80 fallback: SandboxFallback::Allow,
81 }
82 }
83}
84
85impl SandboxSettings {
86 pub fn off() -> Self {
87 Self {
88 mode: SandboxMode::Off,
89 network: true,
90 fallback: SandboxFallback::Allow,
91 }
92 }
93
94 pub fn from_raw(mode: &str, network: bool, fallback: &str) -> Self {
96 let mode = mode.parse().unwrap_or_else(|e| {
97 tracing::warn!("{e}; falling back to workspace");
98 SandboxMode::Workspace
99 });
100 let fallback = fallback.parse().unwrap_or_else(|e| {
101 tracing::warn!("{e}; falling back to allow");
102 SandboxFallback::Allow
103 });
104 Self {
105 mode,
106 network,
107 fallback,
108 }
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn mode_fallback_parse_and_from_raw() {
118 for (raw, mode) in [
119 ("off", SandboxMode::Off),
120 ("none", SandboxMode::Off),
121 ("false", SandboxMode::Off),
122 ("0", SandboxMode::Off),
123 ("workspace", SandboxMode::Workspace),
124 ("on", SandboxMode::Workspace),
125 ("true", SandboxMode::Workspace),
126 ("1", SandboxMode::Workspace),
127 ] {
128 assert_eq!(raw.parse::<SandboxMode>().unwrap(), mode);
129 }
130 assert!("nope".parse::<SandboxMode>().is_err());
131 assert_eq!(SandboxMode::Off.as_str(), "off");
132 assert_eq!(SandboxMode::Workspace.as_str(), "workspace");
133 assert_eq!(SandboxMode::default(), SandboxMode::Workspace);
134
135 for (raw, fb) in [
136 ("allow", SandboxFallback::Allow),
137 ("warn", SandboxFallback::Allow),
138 ("host", SandboxFallback::Allow),
139 ("deny", SandboxFallback::Deny),
140 ("error", SandboxFallback::Deny),
141 ("strict", SandboxFallback::Deny),
142 ] {
143 assert_eq!(raw.parse::<SandboxFallback>().unwrap(), fb);
144 }
145 assert!("nope".parse::<SandboxFallback>().is_err());
146
147 let off = SandboxSettings::off();
148 assert_eq!(off.mode, SandboxMode::Off);
149 assert!(SandboxSettings::default().network);
150 let parsed = SandboxSettings::from_raw("off", false, "deny");
151 assert_eq!(parsed.mode, SandboxMode::Off);
152 assert!(!parsed.network);
153 assert_eq!(parsed.fallback, SandboxFallback::Deny);
154 let fallback = SandboxSettings::from_raw("???", true, "???");
155 assert_eq!(fallback.mode, SandboxMode::Workspace);
156 assert_eq!(fallback.fallback, SandboxFallback::Allow);
157 }
158}