1use crate::args_parser::WrapperOptions;
11use std::collections::HashMap;
12
13pub fn docker_runtime_status_lines(
17 volumes: &[String],
18 mounts: &[String],
19 env: &[String],
20 privileged: bool,
21 network: Option<&str>,
22 networks: &[String],
23 network_aliases: &[String],
24) -> Vec<String> {
25 let mut lines = Vec::new();
26 if !volumes.is_empty() {
27 lines.push(format!("[Isolation] Volumes: {}", volumes.join(", ")));
28 }
29 if !mounts.is_empty() {
30 lines.push(format!("[Isolation] Mounts: {}", mounts.join(", ")));
31 }
32 if !env.is_empty() {
33 lines.push(format!("[Isolation] Env: {}", env.join(", ")));
34 }
35 if privileged {
36 lines.push("[Isolation] Privileged: true".to_string());
37 }
38 let resolved_networks = if networks.is_empty() {
39 network.into_iter().collect::<Vec<_>>()
40 } else {
41 networks.iter().map(String::as_str).collect::<Vec<_>>()
42 };
43 if let Some(first_network) = resolved_networks.first() {
44 lines.push(format!("[Isolation] Network: {}", first_network));
45 }
46 if resolved_networks.len() > 1 {
47 lines.push(format!(
48 "[Isolation] Networks: {}",
49 resolved_networks.join(", ")
50 ));
51 }
52 if !network_aliases.is_empty() {
53 lines.push(format!(
54 "[Isolation] Network aliases: {}",
55 network_aliases.join(", ")
56 ));
57 }
58 lines
59}
60
61pub fn docker_runtime_status_lines_for_options(options: &WrapperOptions) -> Vec<String> {
63 let mut lines = docker_runtime_status_lines(
64 &options.volumes,
65 &options.mounts,
66 &options.env,
67 options.privileged,
68 options.network.as_deref(),
69 &options.networks,
70 &options.network_aliases,
71 );
72 lines.extend(recovery_status_lines(
73 options.on_kill_resume,
74 options.recovery_command.as_deref(),
75 ));
76 lines
77}
78
79pub fn recovery_status_lines(
82 on_kill_resume: Option<u32>,
83 recovery_command: Option<&str>,
84) -> Vec<String> {
85 let Some(count) = effective_on_kill_resume(on_kill_resume, recovery_command) else {
86 return Vec::new();
87 };
88 vec![format!(
89 "[Isolation] On kill: resume up to {} time(s) with {}",
90 count,
91 recovery_command.unwrap_or("the original command")
92 )]
93}
94
95fn effective_on_kill_resume(
96 on_kill_resume: Option<u32>,
97 recovery_command: Option<&str>,
98) -> Option<u32> {
99 on_kill_resume
100 .filter(|count| *count > 0)
101 .or_else(|| recovery_command.map(|_| 1))
102}
103
104pub fn recovery_metadata(
106 on_kill_resume: Option<u32>,
107 recovery_command: Option<&str>,
108) -> Vec<(String, serde_json::Value)> {
109 let mut entries = Vec::new();
110 if let Some(count) = effective_on_kill_resume(on_kill_resume, recovery_command) {
111 entries.push(("onKillResume".to_string(), serde_json::json!(count)));
112 }
113 if let Some(command) = recovery_command {
114 entries.push(("recoveryCommand".to_string(), serde_json::json!(command)));
115 }
116 entries
117}
118
119pub fn docker_runtime_metadata(
123 volumes: &[String],
124 mounts: &[String],
125 env: &[String],
126 privileged: bool,
127 network: Option<&str>,
128 networks: &[String],
129 network_aliases: &[String],
130) -> Vec<(String, serde_json::Value)> {
131 let arr = |items: &[String]| {
132 serde_json::Value::Array(
133 items
134 .iter()
135 .map(|s| serde_json::Value::String(s.clone()))
136 .collect(),
137 )
138 };
139 let mut entries = Vec::new();
140 if !volumes.is_empty() {
141 entries.push(("volumes".to_string(), arr(volumes)));
142 }
143 if !mounts.is_empty() {
144 entries.push(("mounts".to_string(), arr(mounts)));
145 }
146 if !env.is_empty() {
147 entries.push(("env".to_string(), arr(env)));
148 }
149 if privileged {
150 entries.push(("privileged".to_string(), serde_json::Value::Bool(true)));
151 }
152 let resolved_networks = if networks.is_empty() {
153 network.into_iter().collect::<Vec<_>>()
154 } else {
155 networks.iter().map(String::as_str).collect::<Vec<_>>()
156 };
157 if let Some(network) = resolved_networks.first() {
158 entries.push((
159 "network".to_string(),
160 serde_json::Value::String((*network).to_string()),
161 ));
162 entries.push((
163 "networks".to_string(),
164 serde_json::Value::Array(
165 resolved_networks
166 .iter()
167 .map(|value| serde_json::Value::String((*value).to_string()))
168 .collect(),
169 ),
170 ));
171 }
172 if !network_aliases.is_empty() {
173 entries.push(("networkAliases".to_string(), arr(network_aliases)));
174 }
175 entries
176}
177
178pub fn build_isolation_options_map(
183 environment: Option<&str>,
184 mode: &str,
185 session_name: &str,
186 effective_image: Option<&str>,
187 options: &WrapperOptions,
188 created_user: Option<&str>,
189) -> HashMap<String, serde_json::Value> {
190 let str_val = |s: &str| serde_json::Value::String(s.to_string());
191 let mut opts_map = HashMap::new();
192 if let Some(env) = environment {
193 opts_map.insert("isolated".to_string(), str_val(env));
194 }
195 opts_map.insert("isolationMode".to_string(), str_val(mode));
196 opts_map.insert("sessionName".to_string(), str_val(session_name));
197 if let Some(v) = effective_image {
198 opts_map.insert("image".to_string(), str_val(v));
199 }
200 for (k, v) in docker_runtime_metadata(
201 &options.volumes,
202 &options.mounts,
203 &options.env,
204 options.privileged,
205 options.network.as_deref(),
206 &options.networks,
207 &options.network_aliases,
208 )
209 .into_iter()
210 .chain(recovery_metadata(
211 options.on_kill_resume,
212 options.recovery_command.as_deref(),
213 )) {
214 opts_map.insert(k, v);
215 }
216 if let Some(v) = &options.endpoint {
217 opts_map.insert("endpoint".to_string(), str_val(v));
218 }
219 if let Some(v) = created_user {
220 opts_map.insert("user".to_string(), str_val(v));
221 }
222 opts_map.insert(
223 "keepAlive".to_string(),
224 serde_json::Value::Bool(options.keep_alive),
225 );
226 opts_map.insert(
227 "autoRemoveDockerContainer".to_string(),
228 serde_json::Value::Bool(options.auto_remove_docker_container),
229 );
230 opts_map.insert(
231 "alwaysCleanupContainer".to_string(),
232 serde_json::Value::Bool(options.always_cleanup_container),
233 );
234 opts_map.insert(
235 "keepContainer".to_string(),
236 serde_json::Value::Bool(options.keep_container),
237 );
238 opts_map.insert(
239 "keepContainerOnFail".to_string(),
240 serde_json::Value::Bool(options.keep_container_on_fail),
241 );
242 opts_map
243}
244
245#[cfg(test)]
246#[path = "isolation_metadata_cases.rs"]
247mod tests;