1use std::fmt;
8
9use solti_model::RunnerEnv;
10
11use crate::metrics::MetricsHandle;
12
13#[derive(Clone)]
28pub struct BuildContext {
29 metrics: MetricsHandle,
30 env: RunnerEnv,
31}
32
33impl BuildContext {
34 pub fn new(env: RunnerEnv, metrics: MetricsHandle) -> Self {
36 Self { env, metrics }
37 }
38
39 pub fn env(&self) -> &RunnerEnv {
41 &self.env
42 }
43
44 pub fn metrics(&self) -> &MetricsHandle {
46 &self.metrics
47 }
48
49 pub fn with_env(mut self, env: RunnerEnv) -> Self {
51 self.env = env;
52 self
53 }
54
55 pub fn with_metrics(mut self, metrics: MetricsHandle) -> Self {
57 self.metrics = metrics;
58 self
59 }
60}
61
62impl Default for BuildContext {
63 fn default() -> Self {
64 Self {
65 env: RunnerEnv::default(),
66 metrics: crate::metrics::noop_metrics(),
67 }
68 }
69}
70
71impl fmt::Debug for BuildContext {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 f.debug_struct("BuildContext")
74 .field("env_len", &self.env.len())
75 .field("metrics", &"<handle>")
76 .finish()
77 }
78}
79
80impl fmt::Display for BuildContext {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 write!(f, "BuildContext(env_len={})", self.env.len())
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::BuildContext;
89 use solti_model::RunnerEnv;
90
91 #[test]
92 fn default_build_context_has_empty_env_and_noop_metrics() {
93 let ctx = BuildContext::default();
94 assert_eq!(ctx.env().len(), 0);
95 }
96
97 #[test]
98 fn new_uses_provided_env_and_metrics() {
99 let mut env = RunnerEnv::new();
100 env.push("FOO", "bar");
101 env.push("BAZ", "qux");
102
103 let metrics = crate::metrics::noop_metrics();
104 let ctx = BuildContext::new(env.clone(), metrics);
105
106 assert_eq!(ctx.env().len(), env.len());
107 assert_eq!(ctx.env().get("FOO"), Some("bar"));
108 assert_eq!(ctx.env().get("BAZ"), Some("qux"));
109 }
110
111 #[test]
112 fn with_env_replaces_existing_env() {
113 let mut env1 = RunnerEnv::new();
114 env1.push("FOO", "one");
115
116 let mut env2 = RunnerEnv::new();
117 env2.push("BAR", "two");
118
119 let metrics = crate::metrics::noop_metrics();
120 let ctx = BuildContext::new(env1, metrics).with_env(env2.clone());
121
122 assert_eq!(ctx.env().len(), env2.len());
123 assert!(ctx.env().get("FOO").is_none());
124 assert_eq!(ctx.env().get("BAR"), Some("two"));
125 }
126
127 #[test]
128 fn with_metrics_replaces_backend() {
129 let env = RunnerEnv::new();
130 let metrics1 = crate::metrics::noop_metrics();
131 let metrics2 = crate::metrics::noop_metrics();
132
133 let ctx = BuildContext::new(env, metrics1).with_metrics(metrics2);
134
135 ctx.metrics()
136 .record_task_started(crate::RunnerType::Subprocess);
137 }
138
139 #[test]
140 fn display_includes_env_length() {
141 let mut env = RunnerEnv::new();
142 env.push("FOO", "bar");
143
144 let metrics = crate::metrics::noop_metrics();
145 let ctx = BuildContext::new(env, metrics);
146
147 let s = ctx.to_string();
148 assert_eq!(s, "BuildContext(env_len=1)");
149 }
150
151 #[test]
152 fn metrics_handle_can_be_cloned() {
153 let ctx = BuildContext::default();
154 let handle = ctx.metrics().clone();
155
156 handle.record_task_started(crate::RunnerType::Subprocess);
157 handle.record_task_completed(
158 crate::RunnerType::Subprocess,
159 crate::TaskOutcome::Success,
160 100,
161 );
162 }
163}