1#![warn(missing_docs)]
101#![warn(rustdoc::missing_crate_level_docs)]
102
103pub mod context;
104pub mod environment;
105pub mod inheritance;
106pub mod mounts;
107pub mod providers;
108pub mod resources;
109pub mod runtime;
110pub mod secrets;
111pub mod storage;
112
113pub use context::{ContextMetadata, ExecutionContext};
115pub use environment::{
116 EnvFileRef, EnvValue, EnvironmentConfig, GeneratedValue, SecretRef,
117};
118pub use mounts::{Mount, MountType};
119pub use resources::{
120 CpuConfig, ExecutionLimits, FilesystemConfig, MemoryConfig, NetworkConfig,
121 RateLimit, ResourceConfig,
122};
123pub use runtime::{DockerOverrides, NativeOverrides, RuntimeOverrides, WasmOverrides};
124pub use secrets::{
125 ExternalSecretProvider, SecretDefinition, SecretFileFormat, SecretInjectionTarget,
126 SecretProviderConfig, SecretsConfig,
127};
128
129pub use inheritance::{
131 merge_environments, merge_mounts, merge_resources, merge_secrets, resolve_context,
132 ContextResolver,
133};
134
135pub use storage::{BackupInfo, ContextIndex, ContextIndexEntry, ContextStorage};
137
138pub use providers::{
140 EnvironmentProvider, FileProvider, KeychainProvider, SecretManager, SecretProvider,
141 SecretValue,
142};
143
144pub mod error {
146 use thiserror::Error;
147
148 #[derive(Debug, Error)]
150 pub enum ContextError {
151 #[error("Context not found: {0}")]
153 NotFound(String),
154
155 #[error("Context already exists: {0}")]
157 AlreadyExists(String),
158
159 #[error("Invalid context configuration: {0}")]
161 InvalidConfig(String),
162
163 #[error("Circular inheritance detected: {0}")]
165 CircularInheritance(String),
166
167 #[error("Parent context not found: {0}")]
169 ParentNotFound(String),
170
171 #[error("Secret not found: {0}")]
173 SecretNotFound(String),
174
175 #[error("Required secret not set: {0}")]
177 RequiredSecretNotSet(String),
178
179 #[error("Mount source not found: {0}")]
181 MountSourceNotFound(String),
182
183 #[error("Invalid mount configuration: {0}")]
185 InvalidMount(String),
186
187 #[error("IO error: {0}")]
189 Io(#[from] std::io::Error),
190
191 #[error("Serialization error: {0}")]
193 Serialization(String),
194
195 #[error("Secret provider error: {0}")]
197 SecretProvider(String),
198 }
199
200 impl From<serde_json::Error> for ContextError {
201 fn from(e: serde_json::Error) -> Self {
202 Self::Serialization(e.to_string())
203 }
204 }
205
206 impl From<toml::de::Error> for ContextError {
207 fn from(e: toml::de::Error) -> Self {
208 Self::Serialization(e.to_string())
209 }
210 }
211
212 impl From<toml::ser::Error> for ContextError {
213 fn from(e: toml::ser::Error) -> Self {
214 Self::Serialization(e.to_string())
215 }
216 }
217}
218
219pub use error::ContextError;
220
221pub type Result<T> = std::result::Result<T, ContextError>;
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn test_full_context_creation() {
230 let context = ExecutionContext::new("test-context", "Test Context")
231 .with_description("A comprehensive test context")
232 .with_mount(
233 Mount::directory("data", "/host/data", "/app/data")
234 .as_read_write()
235 .with_description("Data directory"),
236 )
237 .with_mount(
238 Mount::tmpfs("temp", "/tmp", 100)
239 .as_optional(),
240 )
241 .with_environment(
242 EnvironmentConfig::new()
243 .with_var("LOG_LEVEL", "debug")
244 .with_var("APP_ENV", "test")
245 .with_passthrough_prefix("AWS_")
246 .with_passthrough_var("PATH"),
247 )
248 .with_secrets(
249 SecretsConfig::new()
250 .with_required_env_secret("api-key", "API_KEY", "API key for auth"),
251 )
252 .with_resources(
253 ResourceConfig::new()
254 .with_cpu_limit("2")
255 .with_memory_limit("1g")
256 .with_network_enabled()
257 .with_timeout(300),
258 )
259 .with_runtime_overrides(
260 RuntimeOverrides::new()
261 .with_docker(
262 DockerOverrides::new()
263 .with_user("1000:1000")
264 .with_no_new_privileges(),
265 ),
266 )
267 .with_tag("test")
268 .with_tag("comprehensive");
269
270 assert_eq!(context.id, "test-context");
272 assert_eq!(context.name, "Test Context");
273 assert_eq!(context.mounts.len(), 2);
274 assert_eq!(context.environment.variables.len(), 2);
275 assert!(!context.secrets.is_empty());
276 assert!(context.resources.cpu.is_some());
277 assert!(context.resources.memory.is_some());
278 assert!(context.resources.network.enabled);
279 assert!(context.runtime_overrides.is_some());
280 assert_eq!(context.metadata.tags.len(), 2);
281 }
282
283 #[test]
284 fn test_context_inheritance_setup() {
285 let base = ExecutionContext::new("base", "Base Context")
286 .with_environment(
287 EnvironmentConfig::new()
288 .with_var("BASE_VAR", "base_value"),
289 );
290
291 let child = ExecutionContext::inheriting("child", "Child Context", "base")
292 .with_environment(
293 EnvironmentConfig::new()
294 .with_var("CHILD_VAR", "child_value"),
295 );
296
297 assert!(!base.has_parent());
298 assert!(child.has_parent());
299 assert_eq!(child.inherits_from, Some("base".to_string()));
300 }
301
302 #[test]
303 fn test_full_serialization_roundtrip() {
304 let context = ExecutionContext::new("roundtrip-test", "Roundtrip Test")
305 .with_mount(Mount::directory("data", "/host", "/container"))
306 .with_environment(
307 EnvironmentConfig::new()
308 .with_var("KEY", "value"),
309 )
310 .with_secrets(
311 SecretsConfig::new()
312 .with_required_env_secret("secret", "SECRET_VAR", "A secret"),
313 )
314 .with_resources(
315 ResourceConfig::new()
316 .with_memory_limit("512m"),
317 );
318
319 let json = serde_json::to_string_pretty(&context).unwrap();
321 let from_json: ExecutionContext = serde_json::from_str(&json).unwrap();
322 assert_eq!(context.id, from_json.id);
323 assert_eq!(context.mounts.len(), from_json.mounts.len());
324
325 let toml_str = toml::to_string_pretty(&context).unwrap();
327 let from_toml: ExecutionContext = toml::from_str(&toml_str).unwrap();
328 assert_eq!(context.id, from_toml.id);
329 assert_eq!(context.mounts.len(), from_toml.mounts.len());
330 }
331}