1use serde::{Deserialize, Serialize};
17
18#[derive(Clone, Debug, Default, Deserialize, Serialize)]
20#[serde(deny_unknown_fields)]
21pub struct AppConfig {
22 #[serde(default)]
24 pub name: Option<String>,
25 #[serde(default)]
26 pub sandbox: SandboxConfig,
27 #[serde(default)]
28 pub secrets: SecretsConfig,
29 #[serde(default)]
30 pub deploy: DeployConfig,
31}
32
33#[derive(Clone, Debug, Default, Deserialize, Serialize)]
35#[serde(deny_unknown_fields)]
36pub struct SandboxConfig {
37 #[serde(default)]
38 pub kind: SandboxKind,
39}
40
41#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
44#[serde(rename_all = "lowercase")]
45pub enum SandboxKind {
46 #[default]
48 None,
49 Local,
51 Bashkit,
53 Microvm,
55}
56
57impl SandboxKind {
58 pub fn as_str(&self) -> &'static str {
59 match self {
60 SandboxKind::None => "none",
61 SandboxKind::Local => "local",
62 SandboxKind::Bashkit => "bashkit",
63 SandboxKind::Microvm => "microvm",
64 }
65 }
66}
67
68#[derive(Clone, Debug, Default, Deserialize, Serialize)]
70#[serde(deny_unknown_fields)]
71pub struct SecretsConfig {
72 #[serde(default)]
73 pub required: Vec<String>,
74}
75
76#[derive(Clone, Debug, Default, Deserialize, Serialize)]
78#[serde(deny_unknown_fields)]
79pub struct DeployConfig {
80 #[serde(default)]
81 pub target: Option<String>,
82}
83
84impl AppConfig {
85 pub fn parse(text: &str) -> crate::Result<Self> {
86 Ok(toml::from_str(text)?)
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn parses_full_config() {
96 let config = AppConfig::parse(
97 r#"
98 name = "revenue-analyst"
99 [sandbox]
100 kind = "bashkit"
101 [secrets]
102 required = ["WAREHOUSE_URL"]
103 [deploy]
104 target = "everruns-cloud"
105 "#,
106 )
107 .unwrap();
108 assert_eq!(config.name.as_deref(), Some("revenue-analyst"));
109 assert_eq!(config.sandbox.kind, SandboxKind::Bashkit);
110 assert_eq!(config.secrets.required, vec!["WAREHOUSE_URL"]);
111 }
112
113 #[test]
114 fn empty_config_defaults_to_no_sandbox() {
115 let config = AppConfig::parse("").unwrap();
116 assert_eq!(config.sandbox.kind, SandboxKind::None);
117 }
118
119 #[test]
120 fn unknown_keys_are_rejected() {
121 assert!(AppConfig::parse("sandbx = 1").is_err());
122 }
123}