1use crate::config::{Config, ValidatedConfig};
23use crate::doctor::{run_doctor, DoctorReport};
24use crate::error::Result;
25use crate::observability::{Metrics, MetricsSnapshot};
26use crate::runtime::{GovernorConfig, ResourceGovernor};
27use crate::support::{build_support_bundle, SupportBundle};
28use std::sync::atomic::{AtomicBool, Ordering};
29use std::sync::Arc;
30
31pub struct AurumEngine {
33 config: ValidatedConfig,
34 governor: Arc<ResourceGovernor>,
35 metrics: Arc<Metrics>,
36 closed: AtomicBool,
37}
38
39impl std::fmt::Debug for AurumEngine {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 f.debug_struct("AurumEngine")
42 .field("config", &self.config)
43 .field("closed", &self.closed.load(Ordering::SeqCst))
44 .field("metrics", &self.metrics.snapshot())
45 .finish_non_exhaustive()
46 }
47}
48
49impl AurumEngine {
50 pub fn new(config: ValidatedConfig) -> Self {
52 Self::with_governor(config, GovernorConfig::default())
53 }
54
55 pub fn with_governor(config: ValidatedConfig, gov: GovernorConfig) -> Self {
57 Self {
58 config,
59 governor: Arc::new(ResourceGovernor::new(gov)),
60 metrics: Arc::new(Metrics::new()),
61 closed: AtomicBool::new(false),
62 }
63 }
64
65 pub fn load() -> Result<Self> {
67 Ok(Self::new(ValidatedConfig::load()?))
68 }
69
70 pub fn load_from_required(path: &std::path::Path) -> Result<Self> {
72 Ok(Self::new(ValidatedConfig::load_from_required(path)?))
73 }
74
75 pub fn from_config(cfg: Config) -> Result<Self> {
77 Ok(Self::new(ValidatedConfig::try_from_config(cfg)?))
78 }
79
80 pub fn config(&self) -> &Config {
81 self.config.as_ref()
82 }
83
84 pub fn validated_config(&self) -> &ValidatedConfig {
85 &self.config
86 }
87
88 pub fn governor(&self) -> &Arc<ResourceGovernor> {
89 &self.governor
90 }
91
92 pub fn metrics(&self) -> &Arc<Metrics> {
93 &self.metrics
94 }
95
96 pub fn is_closed(&self) -> bool {
97 self.closed.load(Ordering::SeqCst)
98 }
99
100 pub fn shutdown(&self) {
104 self.closed.store(true, Ordering::SeqCst);
105 }
106
107 pub fn doctor(&self) -> DoctorReport {
109 run_doctor(self.config.as_ref())
110 }
111
112 pub fn support_bundle(&self, user_notes: Option<String>) -> SupportBundle {
114 let mut bundle = build_support_bundle(self.config.as_ref(), user_notes);
115 bundle.metrics = self.metrics.snapshot();
117 bundle
118 .redaction_notes
119 .push("metrics are engine-local (not process-global)".into());
120 bundle
121 }
122
123 pub fn metrics_snapshot(&self) -> MetricsSnapshot {
124 self.metrics.snapshot()
125 }
126
127 pub fn cache_dir(&self) -> &std::path::Path {
129 &self.config.as_ref().cache_dir
130 }
131}
132
133impl Drop for AurumEngine {
134 fn drop(&mut self) {
135 self.closed.store(true, Ordering::SeqCst);
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn independent_engines_have_independent_metrics() {
145 let a = AurumEngine::load().unwrap();
146 let b = AurumEngine::load().unwrap();
147 a.metrics().record_start();
148 a.metrics()
149 .record_complete(std::time::Duration::from_millis(1));
150 assert_eq!(a.metrics_snapshot().ops_started, 1);
151 assert_eq!(b.metrics_snapshot().ops_started, 0);
152 assert!(!std::ptr::eq(
153 Arc::as_ptr(a.governor()),
154 Arc::as_ptr(b.governor())
155 ));
156 }
157
158 #[test]
159 fn shutdown_flags_closed() {
160 let e = AurumEngine::load().unwrap();
161 assert!(!e.is_closed());
162 e.shutdown();
163 assert!(e.is_closed());
164 }
165
166 #[test]
167 fn doctor_and_support_bundle_work() {
168 let e = AurumEngine::load().unwrap();
169 let d = e.doctor();
170 assert!(!d.checks.is_empty());
171 let b = e.support_bundle(None);
172 assert_eq!(b.schema_version, crate::support::SUPPORT_BUNDLE_VERSION);
173 let json = b.to_json_pretty().unwrap();
174 assert!(json.contains("engine-local"));
175 }
176}