docker_wrapper/command/
run.rs

1//! Docker run command implementation.
2//!
3//! This module provides a comprehensive implementation of the `docker run` command
4//! with support for common options and an extensible architecture for any additional options.
5
6use super::{CommandExecutor, DockerCommand, EnvironmentBuilder, PortBuilder};
7use crate::error::{Error, Result};
8use async_trait::async_trait;
9use std::ffi::OsStr;
10use std::path::PathBuf;
11
12/// Docker run command builder with fluent API
13#[derive(Debug, Clone)]
14#[allow(clippy::struct_excessive_bools)]
15pub struct RunCommand {
16    /// The Docker image to run
17    image: String,
18    /// Command executor for extensibility
19    executor: CommandExecutor,
20    /// Container name
21    name: Option<String>,
22    /// Run in detached mode
23    detach: bool,
24    /// Environment variables
25    environment: EnvironmentBuilder,
26    /// Port mappings
27    ports: PortBuilder,
28    /// Volume mounts
29    volumes: Vec<VolumeMount>,
30    /// Working directory
31    workdir: Option<PathBuf>,
32    /// Entrypoint override
33    entrypoint: Option<String>,
34    /// Command to run in container
35    command: Option<Vec<String>>,
36    /// Interactive mode
37    interactive: bool,
38    /// Allocate TTY
39    tty: bool,
40    /// Remove container on exit
41    remove: bool,
42
43    // Resource Limits
44    /// Memory limit
45    memory: Option<String>,
46    /// Number of CPUs
47    cpus: Option<String>,
48    /// CPU shares (relative weight)
49    cpu_shares: Option<i64>,
50    /// CPU CFS period
51    cpu_period: Option<i64>,
52    /// CPU CFS quota
53    cpu_quota: Option<i64>,
54    /// CPUs in which to allow execution
55    cpuset_cpus: Option<String>,
56    /// MEMs in which to allow execution
57    cpuset_mems: Option<String>,
58    /// Memory + swap limit
59    memory_swap: Option<String>,
60    /// Memory soft limit
61    memory_reservation: Option<String>,
62
63    // Security & User Context
64    /// Username or UID
65    user: Option<String>,
66    /// Give extended privileges
67    privileged: bool,
68    /// Container host name
69    hostname: Option<String>,
70
71    // Lifecycle Management
72    /// Restart policy
73    restart: Option<String>,
74
75    // System Integration
76    /// Set platform if server is multi-platform capable
77    platform: Option<String>,
78    /// Runtime to use for this container
79    runtime: Option<String>,
80    /// Container isolation technology
81    isolation: Option<String>,
82    /// Pull image before running
83    pull: Option<String>,
84    /// Write the container ID to the file
85    cidfile: Option<String>,
86    /// Container NIS domain name
87    domainname: Option<String>,
88    /// Container MAC address
89    mac_address: Option<String>,
90
91    // Logging & Drivers
92    /// Logging driver for the container
93    log_driver: Option<String>,
94    /// Optional volume driver for the container
95    volume_driver: Option<String>,
96
97    // Namespaces
98    /// User namespace to use
99    userns: Option<String>,
100    /// UTS namespace to use
101    uts: Option<String>,
102    /// PID namespace to use
103    pid: Option<String>,
104    /// IPC mode to use
105    ipc: Option<String>,
106    /// Cgroup namespace to use
107    cgroupns: Option<String>,
108    /// Optional parent cgroup for the container
109    cgroup_parent: Option<String>,
110
111    // Advanced Memory & Performance
112    /// Kernel memory limit
113    kernel_memory: Option<String>,
114    /// Tune container memory swappiness (0 to 100)
115    memory_swappiness: Option<i32>,
116    /// Tune host's OOM preferences (-1000 to 1000)
117    oom_score_adj: Option<i32>,
118    /// Tune container pids limit
119    pids_limit: Option<i64>,
120    /// Size of /dev/shm
121    shm_size: Option<String>,
122
123    // Process Control
124    /// Signal to stop the container
125    stop_signal: Option<String>,
126    /// Timeout (in seconds) to stop a container
127    stop_timeout: Option<i32>,
128    /// Override the key sequence for detaching a container
129    detach_keys: Option<String>,
130
131    // Simple Flags
132    /// Proxy received signals to the process
133    sig_proxy: bool,
134    /// Mount the container's root filesystem as read only
135    read_only: bool,
136    /// Run an init inside the container
137    init: bool,
138    /// Disable OOM Killer
139    oom_kill_disable: bool,
140    /// Disable any container-specified HEALTHCHECK
141    no_healthcheck: bool,
142    /// Skip image verification
143    disable_content_trust: bool,
144    /// Publish all exposed ports to random ports
145    publish_all: bool,
146    /// Suppress the pull output
147    quiet: bool,
148
149    // High-Impact List Options
150    // DNS & Network
151    /// Custom DNS servers
152    dns: Vec<String>,
153    /// DNS options
154    dns_option: Vec<String>,
155    /// DNS search domains
156    dns_search: Vec<String>,
157    /// Add host-to-IP mappings (host:ip)
158    add_host: Vec<String>,
159
160    // Security & Capabilities
161    /// Add Linux capabilities
162    cap_add: Vec<String>,
163    /// Drop Linux capabilities
164    cap_drop: Vec<String>,
165    /// Security options
166    security_opt: Vec<String>,
167
168    // Device & Filesystem
169    /// Add host devices to container
170    device: Vec<String>,
171    /// Mount tmpfs directories
172    tmpfs: Vec<String>,
173    /// Expose ports without publishing them
174    expose: Vec<String>,
175
176    // Environment & Labels
177    /// Read environment from files
178    env_file: Vec<PathBuf>,
179    /// Set metadata labels
180    label: Vec<String>,
181    /// Read labels from files
182    label_file: Vec<PathBuf>,
183
184    // Additional List/Vec Options
185    /// Network aliases for the container
186    network_alias: Vec<String>,
187    /// Additional groups for the user
188    group_add: Vec<String>,
189    /// Attach to STDIN, STDOUT or STDERR
190    attach: Vec<String>,
191    /// Log driver options
192    log_opt: Vec<String>,
193    /// Storage driver options
194    storage_opt: Vec<String>,
195    /// Ulimit options
196    ulimit: Vec<String>,
197    /// Mount volumes from other containers
198    volumes_from: Vec<String>,
199    /// Add link to another container (deprecated)
200    link: Vec<String>,
201    /// Container IPv4/IPv6 link-local addresses
202    link_local_ip: Vec<String>,
203
204    // Health Check Options
205    // Health checks
206    /// Command to run to check health
207    health_cmd: Option<String>,
208    /// Time between running the check (ms|s|m|h)
209    health_interval: Option<String>,
210    /// Consecutive failures needed to report unhealthy
211    health_retries: Option<i32>,
212    /// Maximum time to allow one check to run (ms|s|m|h)
213    health_timeout: Option<String>,
214    /// Start period for the container to initialize before health-checking (ms|s|m|h)
215    health_start_period: Option<String>,
216    /// Time between health checks during the start period (ms|s|m|h)
217    health_start_interval: Option<String>,
218
219    // Advanced options
220    /// Advanced mount configuration
221    mount: Vec<String>,
222    /// Connect to a network
223    network: Vec<String>,
224    /// GPU devices to add to the container
225    gpus: Option<String>,
226
227    // Map-based options (stored as Vec<String> in key=value format)
228    /// Add custom annotations
229    annotation: Vec<String>,
230    /// Kernel parameters to set
231    sysctl: Vec<String>,
232
233    // Advanced System Options
234    // Block I/O controls
235    /// Block IO weight (relative weight)
236    blkio_weight: Option<u16>,
237    /// Block IO weight per device
238    blkio_weight_device: Vec<String>,
239    /// Limit read rate (bytes per second) from a device
240    device_read_bps: Vec<String>,
241    /// Limit write rate (bytes per second) to a device
242    device_write_bps: Vec<String>,
243    /// Limit read rate (IO per second) from a device
244    device_read_iops: Vec<String>,
245    /// Limit write rate (IO per second) to a device
246    device_write_iops: Vec<String>,
247
248    // Real-time CPU scheduling
249    /// Limit CPU real-time period in microseconds
250    cpu_rt_period: Option<i64>,
251    /// Limit CPU real-time runtime in microseconds
252    cpu_rt_runtime: Option<i64>,
253
254    // Advanced networking
255    /// Container IPv4 address
256    ip: Option<String>,
257    /// Container IPv6 address
258    ip6: Option<String>,
259
260    // Advanced system options
261    /// Cgroup rule for devices
262    device_cgroup_rule: Vec<String>,
263}
264
265/// Volume mount configuration
266#[derive(Debug, Clone)]
267pub struct VolumeMount {
268    /// Source path on host or volume name
269    pub source: String,
270    /// Target path in container
271    pub target: String,
272    /// Mount type (bind, volume, tmpfs)
273    pub mount_type: MountType,
274    /// Read-only mount
275    pub readonly: bool,
276}
277
278/// Type of volume mount
279#[derive(Debug, Clone, Copy, PartialEq, Eq)]
280pub enum MountType {
281    /// Bind mount from host filesystem
282    Bind,
283    /// Named volume
284    Volume,
285    /// Temporary filesystem
286    Tmpfs,
287}
288
289impl std::fmt::Display for VolumeMount {
290    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
291        let readonly_suffix = if self.readonly { ":ro" } else { "" };
292        write!(f, "{}:{}{}", self.source, self.target, readonly_suffix)
293    }
294}
295
296/// Container ID returned by docker run
297#[derive(Debug, Clone, PartialEq, Eq)]
298pub struct ContainerId(pub String);
299
300impl ContainerId {
301    /// Get the container ID as a string
302    #[must_use]
303    pub fn as_str(&self) -> &str {
304        &self.0
305    }
306
307    /// Get the short form of the container ID (first 12 characters)
308    #[must_use]
309    pub fn short(&self) -> &str {
310        if self.0.len() >= 12 {
311            &self.0[..12]
312        } else {
313            &self.0
314        }
315    }
316}
317
318impl std::fmt::Display for ContainerId {
319    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320        write!(f, "{}", self.0)
321    }
322}
323
324impl RunCommand {
325    /// Create a new run command for the specified image
326    #[allow(clippy::too_many_lines)]
327    pub fn new(image: impl Into<String>) -> Self {
328        Self {
329            image: image.into(),
330            executor: CommandExecutor::new(),
331            name: None,
332            detach: false,
333            environment: EnvironmentBuilder::new(),
334            ports: PortBuilder::new(),
335            volumes: Vec::new(),
336            workdir: None,
337            entrypoint: None,
338            command: None,
339            interactive: false,
340            tty: false,
341            remove: false,
342
343            // Resource Limits
344            memory: None,
345            cpus: None,
346            cpu_shares: None,
347            cpu_period: None,
348            cpu_quota: None,
349            cpuset_cpus: None,
350            cpuset_mems: None,
351            memory_swap: None,
352            memory_reservation: None,
353
354            // Security & User Context
355            user: None,
356            privileged: false,
357            hostname: None,
358
359            // Lifecycle Management
360            restart: None,
361
362            // System Integration
363            platform: None,
364            runtime: None,
365            isolation: None,
366            pull: None,
367            cidfile: None,
368            domainname: None,
369            mac_address: None,
370
371            // Logging & Drivers
372            log_driver: None,
373            volume_driver: None,
374
375            // Namespaces
376            userns: None,
377            uts: None,
378            pid: None,
379            ipc: None,
380            cgroupns: None,
381            cgroup_parent: None,
382
383            // Advanced Memory & Performance
384            kernel_memory: None,
385            memory_swappiness: None,
386            oom_score_adj: None,
387            pids_limit: None,
388            shm_size: None,
389
390            // Process Control
391            stop_signal: None,
392            stop_timeout: None,
393            detach_keys: None,
394
395            // Simple Flags
396            sig_proxy: true, // Default is true in Docker
397            read_only: false,
398            init: false,
399            oom_kill_disable: false,
400            no_healthcheck: false,
401            disable_content_trust: true, // Default is true in Docker
402            publish_all: false,
403            quiet: false,
404
405            // High-Impact List Options
406            // DNS & Network
407            dns: Vec::new(),
408            dns_option: Vec::new(),
409            dns_search: Vec::new(),
410            add_host: Vec::new(),
411
412            // Security & Capabilities
413            cap_add: Vec::new(),
414            cap_drop: Vec::new(),
415            security_opt: Vec::new(),
416
417            // Device & Filesystem
418            device: Vec::new(),
419            tmpfs: Vec::new(),
420            expose: Vec::new(),
421
422            // Environment & Labels
423            env_file: Vec::new(),
424            label: Vec::new(),
425            label_file: Vec::new(),
426
427            // Additional List/Vec Options
428            network_alias: Vec::new(),
429            group_add: Vec::new(),
430            attach: Vec::new(),
431            log_opt: Vec::new(),
432            storage_opt: Vec::new(),
433            ulimit: Vec::new(),
434            volumes_from: Vec::new(),
435            link: Vec::new(),
436            link_local_ip: Vec::new(),
437
438            // Health Check Options
439            health_cmd: None,
440            health_interval: None,
441            health_retries: None,
442            health_timeout: None,
443            health_start_period: None,
444            health_start_interval: None,
445            mount: Vec::new(),
446            network: Vec::new(),
447            gpus: None,
448            annotation: Vec::new(),
449            sysctl: Vec::new(),
450
451            // Advanced System Options
452            blkio_weight: None,
453            blkio_weight_device: Vec::new(),
454            device_read_bps: Vec::new(),
455            device_write_bps: Vec::new(),
456            device_read_iops: Vec::new(),
457            device_write_iops: Vec::new(),
458            cpu_rt_period: None,
459            cpu_rt_runtime: None,
460            ip: None,
461            ip6: None,
462            device_cgroup_rule: Vec::new(),
463        }
464    }
465
466    /// Set the container name
467    #[must_use]
468    pub fn name(mut self, name: impl Into<String>) -> Self {
469        self.name = Some(name.into());
470        self
471    }
472
473    /// Run in detached mode (background)
474    #[must_use]
475    pub fn detach(mut self) -> Self {
476        self.detach = true;
477        self
478    }
479
480    /// Add an environment variable
481    #[must_use]
482    pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
483        self.environment = self.environment.var(key, value);
484        self
485    }
486
487    /// Add multiple environment variables
488    #[must_use]
489    pub fn envs(mut self, vars: std::collections::HashMap<String, String>) -> Self {
490        self.environment = self.environment.vars(vars);
491        self
492    }
493
494    /// Add a port mapping
495    #[must_use]
496    pub fn port(mut self, host_port: u16, container_port: u16) -> Self {
497        self.ports = self.ports.port(host_port, container_port);
498        self
499    }
500
501    /// Add a dynamic port mapping (Docker assigns host port)
502    #[must_use]
503    pub fn dynamic_port(mut self, container_port: u16) -> Self {
504        self.ports = self.ports.dynamic_port(container_port);
505        self
506    }
507
508    /// Add a volume mount
509    #[must_use]
510    pub fn volume(mut self, source: impl Into<String>, target: impl Into<String>) -> Self {
511        self.volumes.push(VolumeMount {
512            source: source.into(),
513            target: target.into(),
514            mount_type: MountType::Volume,
515            readonly: false,
516        });
517        self
518    }
519
520    /// Add a bind mount
521    #[must_use]
522    pub fn bind(mut self, source: impl Into<String>, target: impl Into<String>) -> Self {
523        self.volumes.push(VolumeMount {
524            source: source.into(),
525            target: target.into(),
526            mount_type: MountType::Bind,
527            readonly: false,
528        });
529        self
530    }
531
532    /// Add a read-only volume mount
533    #[must_use]
534    pub fn volume_ro(mut self, source: impl Into<String>, target: impl Into<String>) -> Self {
535        self.volumes.push(VolumeMount {
536            source: source.into(),
537            target: target.into(),
538            mount_type: MountType::Volume,
539            readonly: true,
540        });
541        self
542    }
543
544    /// Set working directory
545    #[must_use]
546    pub fn workdir(mut self, workdir: impl Into<PathBuf>) -> Self {
547        self.workdir = Some(workdir.into());
548        self
549    }
550
551    /// Override entrypoint
552    #[must_use]
553    pub fn entrypoint(mut self, entrypoint: impl Into<String>) -> Self {
554        self.entrypoint = Some(entrypoint.into());
555        self
556    }
557
558    /// Set command to run in container
559    #[must_use]
560    pub fn cmd(mut self, command: Vec<String>) -> Self {
561        self.command = Some(command);
562        self
563    }
564
565    /// Enable interactive mode
566    #[must_use]
567    pub fn interactive(mut self) -> Self {
568        self.interactive = true;
569        self
570    }
571
572    /// Allocate a TTY
573    #[must_use]
574    pub fn tty(mut self) -> Self {
575        self.tty = true;
576        self
577    }
578
579    /// Remove container automatically when it exits
580    #[must_use]
581    pub fn remove(mut self) -> Self {
582        self.remove = true;
583        self
584    }
585
586    /// Convenience method for interactive TTY mode
587    #[must_use]
588    pub fn it(self) -> Self {
589        self.interactive().tty()
590    }
591
592    // Resource Limits
593    /// Set memory limit (e.g., "1g", "512m")
594    #[must_use]
595    pub fn memory(mut self, memory: impl Into<String>) -> Self {
596        self.memory = Some(memory.into());
597        self
598    }
599
600    /// Set number of CPUs (e.g., "2.0", "1.5")
601    #[must_use]
602    pub fn cpus(mut self, cpus: impl Into<String>) -> Self {
603        self.cpus = Some(cpus.into());
604        self
605    }
606
607    /// Set CPU shares (relative weight)
608    #[must_use]
609    pub fn cpu_shares(mut self, shares: i64) -> Self {
610        self.cpu_shares = Some(shares);
611        self
612    }
613
614    /// Set CPU CFS period in microseconds
615    #[must_use]
616    pub fn cpu_period(mut self, period: i64) -> Self {
617        self.cpu_period = Some(period);
618        self
619    }
620
621    /// Set CPU CFS quota in microseconds
622    #[must_use]
623    pub fn cpu_quota(mut self, quota: i64) -> Self {
624        self.cpu_quota = Some(quota);
625        self
626    }
627
628    /// Set CPUs in which to allow execution (e.g., "0-3", "0,1")
629    #[must_use]
630    pub fn cpuset_cpus(mut self, cpus: impl Into<String>) -> Self {
631        self.cpuset_cpus = Some(cpus.into());
632        self
633    }
634
635    /// Set MEMs in which to allow execution (e.g., "0-3", "0,1")
636    #[must_use]
637    pub fn cpuset_mems(mut self, mems: impl Into<String>) -> Self {
638        self.cpuset_mems = Some(mems.into());
639        self
640    }
641
642    /// Set memory + swap limit (e.g., "2g", "-1" for unlimited)
643    #[must_use]
644    pub fn memory_swap(mut self, swap: impl Into<String>) -> Self {
645        self.memory_swap = Some(swap.into());
646        self
647    }
648
649    /// Set memory soft limit (e.g., "500m")
650    #[must_use]
651    pub fn memory_reservation(mut self, reservation: impl Into<String>) -> Self {
652        self.memory_reservation = Some(reservation.into());
653        self
654    }
655
656    // Security & User Context
657    /// Set username or UID (format: <name|uid>[:<group|gid>])
658    #[must_use]
659    pub fn user(mut self, user: impl Into<String>) -> Self {
660        self.user = Some(user.into());
661        self
662    }
663
664    /// Give extended privileges to this container
665    #[must_use]
666    pub fn privileged(mut self) -> Self {
667        self.privileged = true;
668        self
669    }
670
671    /// Set container host name
672    #[must_use]
673    pub fn hostname(mut self, hostname: impl Into<String>) -> Self {
674        self.hostname = Some(hostname.into());
675        self
676    }
677
678    // Lifecycle Management
679    /// Set restart policy (e.g., "always", "unless-stopped", "on-failure", "no")
680    #[must_use]
681    pub fn restart(mut self, restart: impl Into<String>) -> Self {
682        self.restart = Some(restart.into());
683        self
684    }
685
686    // System Integration
687    /// Set platform if server is multi-platform capable (e.g., "linux/amd64")
688    #[must_use]
689    pub fn platform(mut self, platform: impl Into<String>) -> Self {
690        self.platform = Some(platform.into());
691        self
692    }
693
694    /// Set runtime to use for this container
695    #[must_use]
696    pub fn runtime(mut self, runtime: impl Into<String>) -> Self {
697        self.runtime = Some(runtime.into());
698        self
699    }
700
701    /// Set container isolation technology
702    #[must_use]
703    pub fn isolation(mut self, isolation: impl Into<String>) -> Self {
704        self.isolation = Some(isolation.into());
705        self
706    }
707
708    /// Set pull image policy ("always", "missing", "never")
709    #[must_use]
710    pub fn pull(mut self, pull: impl Into<String>) -> Self {
711        self.pull = Some(pull.into());
712        self
713    }
714
715    /// Write the container ID to the specified file
716    #[must_use]
717    pub fn cidfile(mut self, cidfile: impl Into<String>) -> Self {
718        self.cidfile = Some(cidfile.into());
719        self
720    }
721
722    /// Set container NIS domain name
723    #[must_use]
724    pub fn domainname(mut self, domainname: impl Into<String>) -> Self {
725        self.domainname = Some(domainname.into());
726        self
727    }
728
729    /// Set container MAC address (e.g., "92:d0:c6:0a:29:33")
730    #[must_use]
731    pub fn mac_address(mut self, mac: impl Into<String>) -> Self {
732        self.mac_address = Some(mac.into());
733        self
734    }
735
736    // Logging & Drivers
737    /// Set logging driver for the container
738    #[must_use]
739    pub fn log_driver(mut self, driver: impl Into<String>) -> Self {
740        self.log_driver = Some(driver.into());
741        self
742    }
743
744    /// Set optional volume driver for the container
745    #[must_use]
746    pub fn volume_driver(mut self, driver: impl Into<String>) -> Self {
747        self.volume_driver = Some(driver.into());
748        self
749    }
750
751    // Namespaces
752    /// Set user namespace to use
753    #[must_use]
754    pub fn userns(mut self, userns: impl Into<String>) -> Self {
755        self.userns = Some(userns.into());
756        self
757    }
758
759    /// Set UTS namespace to use
760    #[must_use]
761    pub fn uts(mut self, uts: impl Into<String>) -> Self {
762        self.uts = Some(uts.into());
763        self
764    }
765
766    /// Set PID namespace to use
767    #[must_use]
768    pub fn pid(mut self, pid: impl Into<String>) -> Self {
769        self.pid = Some(pid.into());
770        self
771    }
772
773    /// Set IPC mode to use
774    #[must_use]
775    pub fn ipc(mut self, ipc: impl Into<String>) -> Self {
776        self.ipc = Some(ipc.into());
777        self
778    }
779
780    /// Set cgroup namespace to use (host|private)
781    #[must_use]
782    pub fn cgroupns(mut self, cgroupns: impl Into<String>) -> Self {
783        self.cgroupns = Some(cgroupns.into());
784        self
785    }
786
787    /// Set optional parent cgroup for the container
788    #[must_use]
789    pub fn cgroup_parent(mut self, parent: impl Into<String>) -> Self {
790        self.cgroup_parent = Some(parent.into());
791        self
792    }
793
794    // Advanced Memory & Performance
795    /// Set kernel memory limit
796    #[must_use]
797    pub fn kernel_memory(mut self, memory: impl Into<String>) -> Self {
798        self.kernel_memory = Some(memory.into());
799        self
800    }
801
802    /// Tune container memory swappiness (0 to 100)
803    #[must_use]
804    pub fn memory_swappiness(mut self, swappiness: i32) -> Self {
805        self.memory_swappiness = Some(swappiness);
806        self
807    }
808
809    /// Tune host's OOM preferences (-1000 to 1000)
810    #[must_use]
811    pub fn oom_score_adj(mut self, score: i32) -> Self {
812        self.oom_score_adj = Some(score);
813        self
814    }
815
816    /// Tune container pids limit (set -1 for unlimited)
817    #[must_use]
818    pub fn pids_limit(mut self, limit: i64) -> Self {
819        self.pids_limit = Some(limit);
820        self
821    }
822
823    /// Set size of /dev/shm (e.g., "64m")
824    #[must_use]
825    pub fn shm_size(mut self, size: impl Into<String>) -> Self {
826        self.shm_size = Some(size.into());
827        self
828    }
829
830    // Process Control
831    /// Set signal to stop the container (e.g., "SIGTERM", "SIGKILL")
832    #[must_use]
833    pub fn stop_signal(mut self, signal: impl Into<String>) -> Self {
834        self.stop_signal = Some(signal.into());
835        self
836    }
837
838    /// Set timeout (in seconds) to stop a container
839    #[must_use]
840    pub fn stop_timeout(mut self, timeout: i32) -> Self {
841        self.stop_timeout = Some(timeout);
842        self
843    }
844
845    /// Override the key sequence for detaching a container
846    #[must_use]
847    pub fn detach_keys(mut self, keys: impl Into<String>) -> Self {
848        self.detach_keys = Some(keys.into());
849        self
850    }
851
852    // Simple Flags
853    /// Disable proxying received signals to the process
854    #[must_use]
855    pub fn no_sig_proxy(mut self) -> Self {
856        self.sig_proxy = false;
857        self
858    }
859
860    /// Mount the container's root filesystem as read only
861    #[must_use]
862    pub fn read_only(mut self) -> Self {
863        self.read_only = true;
864        self
865    }
866
867    /// Run an init inside the container that forwards signals and reaps processes
868    #[must_use]
869    pub fn init(mut self) -> Self {
870        self.init = true;
871        self
872    }
873
874    /// Disable OOM Killer
875    #[must_use]
876    pub fn oom_kill_disable(mut self) -> Self {
877        self.oom_kill_disable = true;
878        self
879    }
880
881    /// Disable any container-specified HEALTHCHECK
882    #[must_use]
883    pub fn no_healthcheck(mut self) -> Self {
884        self.no_healthcheck = true;
885        self
886    }
887
888    /// Enable image verification (disable content trust is false)
889    #[must_use]
890    pub fn enable_content_trust(mut self) -> Self {
891        self.disable_content_trust = false;
892        self
893    }
894
895    /// Publish all exposed ports to random ports
896    #[must_use]
897    pub fn publish_all(mut self) -> Self {
898        self.publish_all = true;
899        self
900    }
901
902    /// Suppress the pull output
903    #[must_use]
904    pub fn quiet(mut self) -> Self {
905        self.quiet = true;
906        self
907    }
908
909    // High-Impact List Options
910
911    // DNS & Network
912    /// Add custom DNS server
913    #[must_use]
914    pub fn dns(mut self, dns: impl Into<String>) -> Self {
915        self.dns.push(dns.into());
916        self
917    }
918
919    /// Add multiple DNS servers
920    #[must_use]
921    pub fn dns_servers(mut self, servers: Vec<String>) -> Self {
922        self.dns.extend(servers);
923        self
924    }
925
926    /// Add DNS option
927    #[must_use]
928    pub fn dns_option(mut self, option: impl Into<String>) -> Self {
929        self.dns_option.push(option.into());
930        self
931    }
932
933    /// Add DNS search domain
934    #[must_use]
935    pub fn dns_search(mut self, domain: impl Into<String>) -> Self {
936        self.dns_search.push(domain.into());
937        self
938    }
939
940    /// Add host-to-IP mapping (format: "hostname:ip")
941    #[must_use]
942    pub fn add_host(mut self, mapping: impl Into<String>) -> Self {
943        self.add_host.push(mapping.into());
944        self
945    }
946
947    // Security & Capabilities
948    /// Add Linux capability
949    #[must_use]
950    pub fn cap_add(mut self, capability: impl Into<String>) -> Self {
951        self.cap_add.push(capability.into());
952        self
953    }
954
955    /// Drop Linux capability
956    #[must_use]
957    pub fn cap_drop(mut self, capability: impl Into<String>) -> Self {
958        self.cap_drop.push(capability.into());
959        self
960    }
961
962    /// Add security option
963    #[must_use]
964    pub fn security_opt(mut self, option: impl Into<String>) -> Self {
965        self.security_opt.push(option.into());
966        self
967    }
968
969    // Device & Filesystem
970    /// Add host device to container
971    #[must_use]
972    pub fn device(mut self, device: impl Into<String>) -> Self {
973        self.device.push(device.into());
974        self
975    }
976
977    /// Mount tmpfs directory
978    #[must_use]
979    pub fn tmpfs(mut self, path: impl Into<String>) -> Self {
980        self.tmpfs.push(path.into());
981        self
982    }
983
984    /// Expose port without publishing it
985    #[must_use]
986    pub fn expose(mut self, port: impl Into<String>) -> Self {
987        self.expose.push(port.into());
988        self
989    }
990
991    // Environment & Labels
992    /// Read environment variables from file
993    #[must_use]
994    pub fn env_file(mut self, file: impl Into<PathBuf>) -> Self {
995        self.env_file.push(file.into());
996        self
997    }
998
999    /// Add metadata label
1000    #[must_use]
1001    pub fn label(mut self, label: impl Into<String>) -> Self {
1002        self.label.push(label.into());
1003        self
1004    }
1005
1006    /// Read labels from file
1007    #[must_use]
1008    pub fn label_file(mut self, file: impl Into<PathBuf>) -> Self {
1009        self.label_file.push(file.into());
1010        self
1011    }
1012
1013    // Additional List/Vec Options
1014
1015    /// Add network alias for the container
1016    #[must_use]
1017    pub fn network_alias(mut self, alias: impl Into<String>) -> Self {
1018        self.network_alias.push(alias.into());
1019        self
1020    }
1021
1022    /// Add supplementary group for the user
1023    #[must_use]
1024    pub fn group_add(mut self, group: impl Into<String>) -> Self {
1025        self.group_add.push(group.into());
1026        self
1027    }
1028
1029    /// Attach to STDIN, STDOUT or STDERR
1030    #[must_use]
1031    pub fn attach(mut self, stream: impl Into<String>) -> Self {
1032        self.attach.push(stream.into());
1033        self
1034    }
1035
1036    /// Add log driver option
1037    #[must_use]
1038    pub fn log_opt(mut self, option: impl Into<String>) -> Self {
1039        self.log_opt.push(option.into());
1040        self
1041    }
1042
1043    /// Add storage driver option
1044    #[must_use]
1045    pub fn storage_opt(mut self, option: impl Into<String>) -> Self {
1046        self.storage_opt.push(option.into());
1047        self
1048    }
1049
1050    /// Set ulimit option
1051    #[must_use]
1052    pub fn ulimit(mut self, limit: impl Into<String>) -> Self {
1053        self.ulimit.push(limit.into());
1054        self
1055    }
1056
1057    /// Mount volumes from another container
1058    #[must_use]
1059    pub fn volumes_from(mut self, container: impl Into<String>) -> Self {
1060        self.volumes_from.push(container.into());
1061        self
1062    }
1063
1064    /// Add link to another container (deprecated)
1065    #[must_use]
1066    pub fn link(mut self, link: impl Into<String>) -> Self {
1067        self.link.push(link.into());
1068        self
1069    }
1070
1071    /// Add container IPv4/IPv6 link-local address
1072    #[must_use]
1073    pub fn link_local_ip(mut self, ip: impl Into<String>) -> Self {
1074        self.link_local_ip.push(ip.into());
1075        self
1076    }
1077
1078    // Health Check Options
1079
1080    // Health check methods
1081    /// Set health check command
1082    #[must_use]
1083    pub fn health_cmd(mut self, cmd: impl Into<String>) -> Self {
1084        self.health_cmd = Some(cmd.into());
1085        self
1086    }
1087
1088    /// Set health check interval
1089    #[must_use]
1090    pub fn health_interval(mut self, interval: impl Into<String>) -> Self {
1091        self.health_interval = Some(interval.into());
1092        self
1093    }
1094
1095    /// Set health check retries
1096    #[must_use]
1097    pub fn health_retries(mut self, retries: i32) -> Self {
1098        self.health_retries = Some(retries);
1099        self
1100    }
1101
1102    /// Set health check timeout
1103    #[must_use]
1104    pub fn health_timeout(mut self, timeout: impl Into<String>) -> Self {
1105        self.health_timeout = Some(timeout.into());
1106        self
1107    }
1108
1109    /// Set health check start period
1110    #[must_use]
1111    pub fn health_start_period(mut self, period: impl Into<String>) -> Self {
1112        self.health_start_period = Some(period.into());
1113        self
1114    }
1115
1116    /// Set health check start interval
1117    #[must_use]
1118    pub fn health_start_interval(mut self, interval: impl Into<String>) -> Self {
1119        self.health_start_interval = Some(interval.into());
1120        self
1121    }
1122
1123    // Advanced options
1124    /// Add advanced mount configuration
1125    #[must_use]
1126    pub fn mount(mut self, mount: impl Into<String>) -> Self {
1127        self.mount.push(mount.into());
1128        self
1129    }
1130
1131    /// Connect to a network
1132    #[must_use]
1133    pub fn network(mut self, network: impl Into<String>) -> Self {
1134        self.network.push(network.into());
1135        self
1136    }
1137
1138    /// Set GPU devices to add to the container
1139    #[must_use]
1140    pub fn gpus(mut self, gpus: impl Into<String>) -> Self {
1141        self.gpus = Some(gpus.into());
1142        self
1143    }
1144
1145    /// Add custom annotation (key=value format)
1146    #[must_use]
1147    pub fn annotation(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1148        self.annotation
1149            .push(format!("{}={}", key.into(), value.into()));
1150        self
1151    }
1152
1153    /// Set kernel parameter (key=value format)
1154    #[must_use]
1155    pub fn sysctl(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1156        self.sysctl.push(format!("{}={}", key.into(), value.into()));
1157        self
1158    }
1159
1160    // Advanced System Options
1161
1162    // Block I/O controls
1163    /// Set block IO weight (10-1000)
1164    #[must_use]
1165    pub fn blkio_weight(mut self, weight: u16) -> Self {
1166        self.blkio_weight = Some(weight);
1167        self
1168    }
1169
1170    /// Set block IO weight for a specific device (format: DEVICE:WEIGHT)
1171    #[must_use]
1172    pub fn blkio_weight_device(mut self, device_weight: impl Into<String>) -> Self {
1173        self.blkio_weight_device.push(device_weight.into());
1174        self
1175    }
1176
1177    /// Limit read rate (bytes per second) from a device (format: DEVICE:RATE)
1178    #[must_use]
1179    pub fn device_read_bps(mut self, device_rate: impl Into<String>) -> Self {
1180        self.device_read_bps.push(device_rate.into());
1181        self
1182    }
1183
1184    /// Limit write rate (bytes per second) to a device (format: DEVICE:RATE)
1185    #[must_use]
1186    pub fn device_write_bps(mut self, device_rate: impl Into<String>) -> Self {
1187        self.device_write_bps.push(device_rate.into());
1188        self
1189    }
1190
1191    /// Limit read rate (IO per second) from a device (format: DEVICE:RATE)
1192    #[must_use]
1193    pub fn device_read_iops(mut self, device_rate: impl Into<String>) -> Self {
1194        self.device_read_iops.push(device_rate.into());
1195        self
1196    }
1197
1198    /// Limit write rate (IO per second) to a device (format: DEVICE:RATE)
1199    #[must_use]
1200    pub fn device_write_iops(mut self, device_rate: impl Into<String>) -> Self {
1201        self.device_write_iops.push(device_rate.into());
1202        self
1203    }
1204
1205    // Real-time CPU scheduling
1206    /// Limit CPU real-time period in microseconds
1207    #[must_use]
1208    pub fn cpu_rt_period(mut self, period: i64) -> Self {
1209        self.cpu_rt_period = Some(period);
1210        self
1211    }
1212
1213    /// Limit CPU real-time runtime in microseconds
1214    #[must_use]
1215    pub fn cpu_rt_runtime(mut self, runtime: i64) -> Self {
1216        self.cpu_rt_runtime = Some(runtime);
1217        self
1218    }
1219
1220    // Advanced networking
1221    /// Set container IPv4 address (e.g., "172.30.100.104")
1222    #[must_use]
1223    pub fn ip(mut self, ip: impl Into<String>) -> Self {
1224        self.ip = Some(ip.into());
1225        self
1226    }
1227
1228    /// Set container IPv6 address (e.g., "`2001:db8::33`")
1229    #[must_use]
1230    pub fn ip6(mut self, ip6: impl Into<String>) -> Self {
1231        self.ip6 = Some(ip6.into());
1232        self
1233    }
1234
1235    /// Add device cgroup rule
1236    #[must_use]
1237    pub fn device_cgroup_rule(mut self, rule: impl Into<String>) -> Self {
1238        self.device_cgroup_rule.push(rule.into());
1239        self
1240    }
1241}
1242
1243#[async_trait]
1244impl DockerCommand for RunCommand {
1245    type Output = ContainerId;
1246
1247    fn command_name(&self) -> &'static str {
1248        "run"
1249    }
1250
1251    #[allow(clippy::too_many_lines)]
1252    fn build_args(&self) -> Vec<String> {
1253        let mut args = Vec::new();
1254
1255        // Add flags
1256        if self.detach {
1257            args.push("--detach".to_string());
1258        }
1259        if self.interactive {
1260            args.push("--interactive".to_string());
1261        }
1262        if self.tty {
1263            args.push("--tty".to_string());
1264        }
1265        if self.remove {
1266            args.push("--rm".to_string());
1267        }
1268
1269        // Add container name
1270        if let Some(ref name) = self.name {
1271            args.push("--name".to_string());
1272            args.push(name.clone());
1273        }
1274
1275        // Add working directory
1276        if let Some(ref workdir) = self.workdir {
1277            args.push("--workdir".to_string());
1278            args.push(workdir.to_string_lossy().to_string());
1279        }
1280
1281        // Add entrypoint
1282        if let Some(ref entrypoint) = self.entrypoint {
1283            args.push("--entrypoint".to_string());
1284            args.push(entrypoint.clone());
1285        }
1286
1287        // Add environment variables
1288        args.extend(self.environment.build_args());
1289
1290        // Add port mappings
1291        args.extend(self.ports.build_args());
1292
1293        // Add volume mounts
1294        for volume in &self.volumes {
1295            args.push("--volume".to_string());
1296            args.push(volume.to_string());
1297        }
1298
1299        // Resource Limits
1300        if let Some(ref memory) = self.memory {
1301            args.push("--memory".to_string());
1302            args.push(memory.clone());
1303        }
1304        if let Some(ref cpus) = self.cpus {
1305            args.push("--cpus".to_string());
1306            args.push(cpus.clone());
1307        }
1308        if let Some(cpu_shares) = self.cpu_shares {
1309            args.push("--cpu-shares".to_string());
1310            args.push(cpu_shares.to_string());
1311        }
1312        if let Some(cpu_period) = self.cpu_period {
1313            args.push("--cpu-period".to_string());
1314            args.push(cpu_period.to_string());
1315        }
1316        if let Some(cpu_quota) = self.cpu_quota {
1317            args.push("--cpu-quota".to_string());
1318            args.push(cpu_quota.to_string());
1319        }
1320        if let Some(ref cpuset_cpus) = self.cpuset_cpus {
1321            args.push("--cpuset-cpus".to_string());
1322            args.push(cpuset_cpus.clone());
1323        }
1324        if let Some(ref cpuset_mems) = self.cpuset_mems {
1325            args.push("--cpuset-mems".to_string());
1326            args.push(cpuset_mems.clone());
1327        }
1328        if let Some(ref memory_swap) = self.memory_swap {
1329            args.push("--memory-swap".to_string());
1330            args.push(memory_swap.clone());
1331        }
1332        if let Some(ref memory_reservation) = self.memory_reservation {
1333            args.push("--memory-reservation".to_string());
1334            args.push(memory_reservation.clone());
1335        }
1336
1337        // Security & User Context
1338        if let Some(ref user) = self.user {
1339            args.push("--user".to_string());
1340            args.push(user.clone());
1341        }
1342        if self.privileged {
1343            args.push("--privileged".to_string());
1344        }
1345        if let Some(ref hostname) = self.hostname {
1346            args.push("--hostname".to_string());
1347            args.push(hostname.clone());
1348        }
1349
1350        // Lifecycle Management
1351        if let Some(ref restart) = self.restart {
1352            args.push("--restart".to_string());
1353            args.push(restart.clone());
1354        }
1355
1356        // System Integration
1357        if let Some(ref platform) = self.platform {
1358            args.push("--platform".to_string());
1359            args.push(platform.clone());
1360        }
1361        if let Some(ref runtime) = self.runtime {
1362            args.push("--runtime".to_string());
1363            args.push(runtime.clone());
1364        }
1365        if let Some(ref isolation) = self.isolation {
1366            args.push("--isolation".to_string());
1367            args.push(isolation.clone());
1368        }
1369        if let Some(ref pull) = self.pull {
1370            args.push("--pull".to_string());
1371            args.push(pull.clone());
1372        }
1373        if let Some(ref cidfile) = self.cidfile {
1374            args.push("--cidfile".to_string());
1375            args.push(cidfile.clone());
1376        }
1377        if let Some(ref domainname) = self.domainname {
1378            args.push("--domainname".to_string());
1379            args.push(domainname.clone());
1380        }
1381        if let Some(ref mac_address) = self.mac_address {
1382            args.push("--mac-address".to_string());
1383            args.push(mac_address.clone());
1384        }
1385
1386        // Logging & Drivers
1387        if let Some(ref log_driver) = self.log_driver {
1388            args.push("--log-driver".to_string());
1389            args.push(log_driver.clone());
1390        }
1391        if let Some(ref volume_driver) = self.volume_driver {
1392            args.push("--volume-driver".to_string());
1393            args.push(volume_driver.clone());
1394        }
1395
1396        // Namespaces
1397        if let Some(ref userns) = self.userns {
1398            args.push("--userns".to_string());
1399            args.push(userns.clone());
1400        }
1401        if let Some(ref uts) = self.uts {
1402            args.push("--uts".to_string());
1403            args.push(uts.clone());
1404        }
1405        if let Some(ref pid) = self.pid {
1406            args.push("--pid".to_string());
1407            args.push(pid.clone());
1408        }
1409        if let Some(ref ipc) = self.ipc {
1410            args.push("--ipc".to_string());
1411            args.push(ipc.clone());
1412        }
1413        if let Some(ref cgroupns) = self.cgroupns {
1414            args.push("--cgroupns".to_string());
1415            args.push(cgroupns.clone());
1416        }
1417        if let Some(ref cgroup_parent) = self.cgroup_parent {
1418            args.push("--cgroup-parent".to_string());
1419            args.push(cgroup_parent.clone());
1420        }
1421
1422        // Advanced Memory & Performance
1423        if let Some(ref kernel_memory) = self.kernel_memory {
1424            args.push("--kernel-memory".to_string());
1425            args.push(kernel_memory.clone());
1426        }
1427        if let Some(memory_swappiness) = self.memory_swappiness {
1428            args.push("--memory-swappiness".to_string());
1429            args.push(memory_swappiness.to_string());
1430        }
1431        if let Some(oom_score_adj) = self.oom_score_adj {
1432            args.push("--oom-score-adj".to_string());
1433            args.push(oom_score_adj.to_string());
1434        }
1435        if let Some(pids_limit) = self.pids_limit {
1436            args.push("--pids-limit".to_string());
1437            args.push(pids_limit.to_string());
1438        }
1439        if let Some(ref shm_size) = self.shm_size {
1440            args.push("--shm-size".to_string());
1441            args.push(shm_size.clone());
1442        }
1443
1444        // Process Control
1445        if let Some(ref stop_signal) = self.stop_signal {
1446            args.push("--stop-signal".to_string());
1447            args.push(stop_signal.clone());
1448        }
1449        if let Some(stop_timeout) = self.stop_timeout {
1450            args.push("--stop-timeout".to_string());
1451            args.push(stop_timeout.to_string());
1452        }
1453        if let Some(ref detach_keys) = self.detach_keys {
1454            args.push("--detach-keys".to_string());
1455            args.push(detach_keys.clone());
1456        }
1457
1458        // Simple Flags
1459        if !self.sig_proxy {
1460            args.push("--sig-proxy=false".to_string());
1461        }
1462        if self.read_only {
1463            args.push("--read-only".to_string());
1464        }
1465        if self.init {
1466            args.push("--init".to_string());
1467        }
1468        if self.oom_kill_disable {
1469            args.push("--oom-kill-disable".to_string());
1470        }
1471        if self.no_healthcheck {
1472            args.push("--no-healthcheck".to_string());
1473        }
1474        if !self.disable_content_trust {
1475            args.push("--disable-content-trust=false".to_string());
1476        }
1477        if self.publish_all {
1478            args.push("--publish-all".to_string());
1479        }
1480        if self.quiet {
1481            args.push("--quiet".to_string());
1482        }
1483
1484        // High-Impact List Options
1485        // DNS & Network
1486        for dns in &self.dns {
1487            args.push("--dns".to_string());
1488            args.push(dns.clone());
1489        }
1490        for dns_option in &self.dns_option {
1491            args.push("--dns-option".to_string());
1492            args.push(dns_option.clone());
1493        }
1494        for dns_search in &self.dns_search {
1495            args.push("--dns-search".to_string());
1496            args.push(dns_search.clone());
1497        }
1498        for add_host in &self.add_host {
1499            args.push("--add-host".to_string());
1500            args.push(add_host.clone());
1501        }
1502
1503        // Security & Capabilities
1504        for cap_add in &self.cap_add {
1505            args.push("--cap-add".to_string());
1506            args.push(cap_add.clone());
1507        }
1508        for cap_drop in &self.cap_drop {
1509            args.push("--cap-drop".to_string());
1510            args.push(cap_drop.clone());
1511        }
1512        for security_opt in &self.security_opt {
1513            args.push("--security-opt".to_string());
1514            args.push(security_opt.clone());
1515        }
1516
1517        // Device & Filesystem
1518        for device in &self.device {
1519            args.push("--device".to_string());
1520            args.push(device.clone());
1521        }
1522        for tmpfs in &self.tmpfs {
1523            args.push("--tmpfs".to_string());
1524            args.push(tmpfs.clone());
1525        }
1526        for expose in &self.expose {
1527            args.push("--expose".to_string());
1528            args.push(expose.clone());
1529        }
1530
1531        // Environment & Labels
1532        for env_file in &self.env_file {
1533            args.push("--env-file".to_string());
1534            args.push(env_file.to_string_lossy().to_string());
1535        }
1536        for label in &self.label {
1537            args.push("--label".to_string());
1538            args.push(label.clone());
1539        }
1540        for label_file in &self.label_file {
1541            args.push("--label-file".to_string());
1542            args.push(label_file.to_string_lossy().to_string());
1543        }
1544
1545        // Additional List/Vec Options
1546        for network_alias in &self.network_alias {
1547            args.push("--network-alias".to_string());
1548            args.push(network_alias.clone());
1549        }
1550        for group_add in &self.group_add {
1551            args.push("--group-add".to_string());
1552            args.push(group_add.clone());
1553        }
1554        for attach in &self.attach {
1555            args.push("--attach".to_string());
1556            args.push(attach.clone());
1557        }
1558        for log_opt in &self.log_opt {
1559            args.push("--log-opt".to_string());
1560            args.push(log_opt.clone());
1561        }
1562        for storage_opt in &self.storage_opt {
1563            args.push("--storage-opt".to_string());
1564            args.push(storage_opt.clone());
1565        }
1566        for ulimit in &self.ulimit {
1567            args.push("--ulimit".to_string());
1568            args.push(ulimit.clone());
1569        }
1570        for volumes_from in &self.volumes_from {
1571            args.push("--volumes-from".to_string());
1572            args.push(volumes_from.clone());
1573        }
1574        for link in &self.link {
1575            args.push("--link".to_string());
1576            args.push(link.clone());
1577        }
1578        for link_local_ip in &self.link_local_ip {
1579            args.push("--link-local-ip".to_string());
1580            args.push(link_local_ip.clone());
1581        }
1582
1583        // Health Check Options
1584        // Health checks
1585        if let Some(ref health_cmd) = self.health_cmd {
1586            args.push("--health-cmd".to_string());
1587            args.push(health_cmd.clone());
1588        }
1589        if let Some(ref health_interval) = self.health_interval {
1590            args.push("--health-interval".to_string());
1591            args.push(health_interval.clone());
1592        }
1593        if let Some(health_retries) = self.health_retries {
1594            args.push("--health-retries".to_string());
1595            args.push(health_retries.to_string());
1596        }
1597        if let Some(ref health_timeout) = self.health_timeout {
1598            args.push("--health-timeout".to_string());
1599            args.push(health_timeout.clone());
1600        }
1601        if let Some(ref health_start_period) = self.health_start_period {
1602            args.push("--health-start-period".to_string());
1603            args.push(health_start_period.clone());
1604        }
1605        if let Some(ref health_start_interval) = self.health_start_interval {
1606            args.push("--health-start-interval".to_string());
1607            args.push(health_start_interval.clone());
1608        }
1609
1610        // Advanced options
1611        for mount in &self.mount {
1612            args.push("--mount".to_string());
1613            args.push(mount.clone());
1614        }
1615        for network in &self.network {
1616            args.push("--network".to_string());
1617            args.push(network.clone());
1618        }
1619        if let Some(ref gpus) = self.gpus {
1620            args.push("--gpus".to_string());
1621            args.push(gpus.clone());
1622        }
1623
1624        // Map-based options
1625        for annotation in &self.annotation {
1626            args.push("--annotation".to_string());
1627            args.push(annotation.clone());
1628        }
1629        for sysctl in &self.sysctl {
1630            args.push("--sysctl".to_string());
1631            args.push(sysctl.clone());
1632        }
1633
1634        // Advanced System Options
1635        // Block I/O controls
1636        if let Some(blkio_weight) = self.blkio_weight {
1637            args.push("--blkio-weight".to_string());
1638            args.push(blkio_weight.to_string());
1639        }
1640        for blkio_weight_device in &self.blkio_weight_device {
1641            args.push("--blkio-weight-device".to_string());
1642            args.push(blkio_weight_device.clone());
1643        }
1644        for device_read_bps in &self.device_read_bps {
1645            args.push("--device-read-bps".to_string());
1646            args.push(device_read_bps.clone());
1647        }
1648        for device_write_bps in &self.device_write_bps {
1649            args.push("--device-write-bps".to_string());
1650            args.push(device_write_bps.clone());
1651        }
1652        for device_read_iops in &self.device_read_iops {
1653            args.push("--device-read-iops".to_string());
1654            args.push(device_read_iops.clone());
1655        }
1656        for device_write_iops in &self.device_write_iops {
1657            args.push("--device-write-iops".to_string());
1658            args.push(device_write_iops.clone());
1659        }
1660
1661        // Real-time CPU scheduling
1662        if let Some(cpu_rt_period) = self.cpu_rt_period {
1663            args.push("--cpu-rt-period".to_string());
1664            args.push(cpu_rt_period.to_string());
1665        }
1666        if let Some(cpu_rt_runtime) = self.cpu_rt_runtime {
1667            args.push("--cpu-rt-runtime".to_string());
1668            args.push(cpu_rt_runtime.to_string());
1669        }
1670
1671        // Advanced networking
1672        if let Some(ref ip) = self.ip {
1673            args.push("--ip".to_string());
1674            args.push(ip.clone());
1675        }
1676        if let Some(ref ip6) = self.ip6 {
1677            args.push("--ip6".to_string());
1678            args.push(ip6.clone());
1679        }
1680
1681        // Advanced system options
1682        for device_cgroup_rule in &self.device_cgroup_rule {
1683            args.push("--device-cgroup-rule".to_string());
1684            args.push(device_cgroup_rule.clone());
1685        }
1686
1687        // Add image
1688        args.push(self.image.clone());
1689
1690        // Add command if specified
1691        if let Some(ref command) = self.command {
1692            args.extend(command.clone());
1693        }
1694
1695        args
1696    }
1697
1698    async fn execute(&self) -> Result<Self::Output> {
1699        let args = self.build_args();
1700        let output = self
1701            .executor
1702            .execute_command(self.command_name(), args)
1703            .await?;
1704
1705        // Parse container ID from output
1706        let container_id = output.stdout.trim().to_string();
1707        if container_id.is_empty() {
1708            return Err(Error::parse_error(
1709                "No container ID returned from docker run",
1710            ));
1711        }
1712
1713        Ok(ContainerId(container_id))
1714    }
1715
1716    fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
1717        self.executor.add_arg(arg);
1718        self
1719    }
1720
1721    fn args<I, S>(&mut self, args: I) -> &mut Self
1722    where
1723        I: IntoIterator<Item = S>,
1724        S: AsRef<OsStr>,
1725    {
1726        self.executor.add_args(args);
1727        self
1728    }
1729
1730    fn flag(&mut self, flag: &str) -> &mut Self {
1731        self.executor.add_flag(flag);
1732        self
1733    }
1734
1735    fn option(&mut self, key: &str, value: &str) -> &mut Self {
1736        self.executor.add_option(key, value);
1737        self
1738    }
1739}
1740
1741#[cfg(test)]
1742mod tests {
1743    use super::*;
1744
1745    #[test]
1746    fn test_run_command_builder() {
1747        let cmd = RunCommand::new("nginx:latest")
1748            .name("test-nginx")
1749            .detach()
1750            .env("ENV_VAR", "value")
1751            .port(8080, 80)
1752            .volume("data", "/var/data")
1753            .workdir("/app")
1754            .remove();
1755
1756        let args = cmd.build_args();
1757
1758        assert!(args.contains(&"--detach".to_string()));
1759        assert!(args.contains(&"--name".to_string()));
1760        assert!(args.contains(&"test-nginx".to_string()));
1761        assert!(args.contains(&"--env".to_string()));
1762        assert!(args.contains(&"ENV_VAR=value".to_string()));
1763        assert!(args.contains(&"--publish".to_string()));
1764        assert!(args.contains(&"8080:80".to_string()));
1765        assert!(args.contains(&"--volume".to_string()));
1766        assert!(args.contains(&"data:/var/data".to_string()));
1767        assert!(args.contains(&"--workdir".to_string()));
1768        assert!(args.contains(&"/app".to_string()));
1769        assert!(args.contains(&"--rm".to_string()));
1770        assert!(args.contains(&"nginx:latest".to_string()));
1771    }
1772
1773    #[test]
1774    fn test_run_command_with_cmd() {
1775        let cmd =
1776            RunCommand::new("alpine:latest").cmd(vec!["echo".to_string(), "hello".to_string()]);
1777
1778        let args = cmd.build_args();
1779        assert!(args.contains(&"alpine:latest".to_string()));
1780        assert!(args.contains(&"echo".to_string()));
1781        assert!(args.contains(&"hello".to_string()));
1782    }
1783
1784    #[test]
1785    fn test_run_command_extensibility() {
1786        let mut cmd = RunCommand::new("test:latest");
1787        cmd.flag("privileged")
1788            .option("memory", "1g")
1789            .arg("--custom-option");
1790
1791        // The extensibility is tested via the trait methods
1792        // Full integration testing would require actual Docker execution
1793    }
1794
1795    #[test]
1796    fn test_volume_mount_display() {
1797        let volume = VolumeMount {
1798            source: "data".to_string(),
1799            target: "/var/data".to_string(),
1800            mount_type: MountType::Volume,
1801            readonly: false,
1802        };
1803        assert_eq!(volume.to_string(), "data:/var/data");
1804
1805        let readonly_volume = VolumeMount {
1806            source: "/host/path".to_string(),
1807            target: "/container/path".to_string(),
1808            mount_type: MountType::Bind,
1809            readonly: true,
1810        };
1811        assert_eq!(readonly_volume.to_string(), "/host/path:/container/path:ro");
1812    }
1813
1814    #[test]
1815    fn test_run_command_resource_limits() {
1816        let cmd = RunCommand::new("alpine:latest")
1817            .memory("1g")
1818            .cpus("2.0")
1819            .cpu_shares(1024)
1820            .cpu_period(100_000)
1821            .cpu_quota(50_000)
1822            .cpuset_cpus("0-3")
1823            .cpuset_mems("0,1")
1824            .memory_swap("2g")
1825            .memory_reservation("500m");
1826
1827        let args = cmd.build_args();
1828
1829        assert!(args.contains(&"--memory".to_string()));
1830        assert!(args.contains(&"1g".to_string()));
1831        assert!(args.contains(&"--cpus".to_string()));
1832        assert!(args.contains(&"2.0".to_string()));
1833        assert!(args.contains(&"--cpu-shares".to_string()));
1834        assert!(args.contains(&"1024".to_string()));
1835        assert!(args.contains(&"--cpu-period".to_string()));
1836        assert!(args.contains(&"100000".to_string()));
1837        assert!(args.contains(&"--cpu-quota".to_string()));
1838        assert!(args.contains(&"50000".to_string()));
1839        assert!(args.contains(&"--cpuset-cpus".to_string()));
1840        assert!(args.contains(&"0-3".to_string()));
1841        assert!(args.contains(&"--cpuset-mems".to_string()));
1842        assert!(args.contains(&"0,1".to_string()));
1843        assert!(args.contains(&"--memory-swap".to_string()));
1844        assert!(args.contains(&"2g".to_string()));
1845        assert!(args.contains(&"--memory-reservation".to_string()));
1846        assert!(args.contains(&"500m".to_string()));
1847    }
1848
1849    #[test]
1850    fn test_run_command_security_and_user() {
1851        let cmd = RunCommand::new("alpine:latest")
1852            .user("1000:1000")
1853            .privileged()
1854            .hostname("test-host");
1855
1856        let args = cmd.build_args();
1857
1858        assert!(args.contains(&"--user".to_string()));
1859        assert!(args.contains(&"1000:1000".to_string()));
1860        assert!(args.contains(&"--privileged".to_string()));
1861        assert!(args.contains(&"--hostname".to_string()));
1862        assert!(args.contains(&"test-host".to_string()));
1863    }
1864
1865    #[test]
1866    fn test_run_command_lifecycle_management() {
1867        let cmd = RunCommand::new("alpine:latest").restart("always");
1868
1869        let args = cmd.build_args();
1870
1871        assert!(args.contains(&"--restart".to_string()));
1872        assert!(args.contains(&"always".to_string()));
1873    }
1874
1875    #[test]
1876    fn test_run_command_system_integration() {
1877        let cmd = RunCommand::new("alpine:latest")
1878            .platform("linux/amd64")
1879            .runtime("runc")
1880            .isolation("default")
1881            .pull("always")
1882            .cidfile("/tmp/container.cid")
1883            .domainname("example.com")
1884            .mac_address("92:d0:c6:0a:29:33");
1885
1886        let args = cmd.build_args();
1887
1888        assert!(args.contains(&"--platform".to_string()));
1889        assert!(args.contains(&"linux/amd64".to_string()));
1890        assert!(args.contains(&"--runtime".to_string()));
1891        assert!(args.contains(&"runc".to_string()));
1892        assert!(args.contains(&"--isolation".to_string()));
1893        assert!(args.contains(&"default".to_string()));
1894        assert!(args.contains(&"--pull".to_string()));
1895        assert!(args.contains(&"always".to_string()));
1896        assert!(args.contains(&"--cidfile".to_string()));
1897        assert!(args.contains(&"/tmp/container.cid".to_string()));
1898        assert!(args.contains(&"--domainname".to_string()));
1899        assert!(args.contains(&"example.com".to_string()));
1900        assert!(args.contains(&"--mac-address".to_string()));
1901        assert!(args.contains(&"92:d0:c6:0a:29:33".to_string()));
1902    }
1903
1904    #[test]
1905    fn test_run_command_logging_and_drivers() {
1906        let cmd = RunCommand::new("alpine:latest")
1907            .log_driver("json-file")
1908            .volume_driver("local");
1909
1910        let args = cmd.build_args();
1911
1912        assert!(args.contains(&"--log-driver".to_string()));
1913        assert!(args.contains(&"json-file".to_string()));
1914        assert!(args.contains(&"--volume-driver".to_string()));
1915        assert!(args.contains(&"local".to_string()));
1916    }
1917
1918    #[test]
1919    fn test_run_command_namespaces() {
1920        let cmd = RunCommand::new("alpine:latest")
1921            .userns("host")
1922            .uts("host")
1923            .pid("host")
1924            .ipc("host")
1925            .cgroupns("private")
1926            .cgroup_parent("/docker");
1927
1928        let args = cmd.build_args();
1929
1930        assert!(args.contains(&"--userns".to_string()));
1931        assert!(args.contains(&"host".to_string()));
1932        assert!(args.contains(&"--uts".to_string()));
1933        assert!(args.contains(&"--pid".to_string()));
1934        assert!(args.contains(&"--ipc".to_string()));
1935        assert!(args.contains(&"--cgroupns".to_string()));
1936        assert!(args.contains(&"private".to_string()));
1937        assert!(args.contains(&"--cgroup-parent".to_string()));
1938        assert!(args.contains(&"/docker".to_string()));
1939    }
1940
1941    #[test]
1942    fn test_run_command_advanced_memory_performance() {
1943        let cmd = RunCommand::new("alpine:latest")
1944            .kernel_memory("100m")
1945            .memory_swappiness(60)
1946            .oom_score_adj(-500)
1947            .pids_limit(100)
1948            .shm_size("64m");
1949
1950        let args = cmd.build_args();
1951
1952        assert!(args.contains(&"--kernel-memory".to_string()));
1953        assert!(args.contains(&"100m".to_string()));
1954        assert!(args.contains(&"--memory-swappiness".to_string()));
1955        assert!(args.contains(&"60".to_string()));
1956        assert!(args.contains(&"--oom-score-adj".to_string()));
1957        assert!(args.contains(&"-500".to_string()));
1958        assert!(args.contains(&"--pids-limit".to_string()));
1959        assert!(args.contains(&"100".to_string()));
1960        assert!(args.contains(&"--shm-size".to_string()));
1961        assert!(args.contains(&"64m".to_string()));
1962    }
1963
1964    #[test]
1965    fn test_run_command_process_control() {
1966        let cmd = RunCommand::new("alpine:latest")
1967            .stop_signal("SIGTERM")
1968            .stop_timeout(10)
1969            .detach_keys("ctrl-p,ctrl-q");
1970
1971        let args = cmd.build_args();
1972
1973        assert!(args.contains(&"--stop-signal".to_string()));
1974        assert!(args.contains(&"SIGTERM".to_string()));
1975        assert!(args.contains(&"--stop-timeout".to_string()));
1976        assert!(args.contains(&"10".to_string()));
1977        assert!(args.contains(&"--detach-keys".to_string()));
1978        assert!(args.contains(&"ctrl-p,ctrl-q".to_string()));
1979    }
1980
1981    #[test]
1982    fn test_run_command_simple_flags() {
1983        let cmd = RunCommand::new("alpine:latest")
1984            .no_sig_proxy()
1985            .read_only()
1986            .init()
1987            .oom_kill_disable()
1988            .no_healthcheck()
1989            .enable_content_trust()
1990            .publish_all()
1991            .quiet();
1992
1993        let args = cmd.build_args();
1994
1995        assert!(args.contains(&"--sig-proxy=false".to_string()));
1996        assert!(args.contains(&"--read-only".to_string()));
1997        assert!(args.contains(&"--init".to_string()));
1998        assert!(args.contains(&"--oom-kill-disable".to_string()));
1999        assert!(args.contains(&"--no-healthcheck".to_string()));
2000        assert!(args.contains(&"--disable-content-trust=false".to_string()));
2001        assert!(args.contains(&"--publish-all".to_string()));
2002        assert!(args.contains(&"--quiet".to_string()));
2003    }
2004
2005    #[test]
2006    fn test_run_command_comprehensive_builder() {
2007        let cmd = RunCommand::new("nginx:latest")
2008            .name("production-nginx")
2009            .detach()
2010            .memory("2g")
2011            .cpus("4.0")
2012            .user("nginx:nginx")
2013            .privileged()
2014            .restart("unless-stopped")
2015            .hostname("web-server")
2016            .platform("linux/amd64")
2017            .env("NGINX_PORT", "8080")
2018            .port(80, 8080)
2019            .volume("nginx-data", "/var/lib/nginx")
2020            .workdir("/usr/share/nginx/html")
2021            .read_only()
2022            .init()
2023            .remove();
2024
2025        let args = cmd.build_args();
2026
2027        // Verify comprehensive options are all present
2028        assert!(args.contains(&"--name".to_string()));
2029        assert!(args.contains(&"production-nginx".to_string()));
2030        assert!(args.contains(&"--detach".to_string()));
2031        assert!(args.contains(&"--memory".to_string()));
2032        assert!(args.contains(&"2g".to_string()));
2033        assert!(args.contains(&"--cpus".to_string()));
2034        assert!(args.contains(&"4.0".to_string()));
2035        assert!(args.contains(&"--user".to_string()));
2036        assert!(args.contains(&"nginx:nginx".to_string()));
2037        assert!(args.contains(&"--privileged".to_string()));
2038        assert!(args.contains(&"--restart".to_string()));
2039        assert!(args.contains(&"unless-stopped".to_string()));
2040        assert!(args.contains(&"--hostname".to_string()));
2041        assert!(args.contains(&"web-server".to_string()));
2042        assert!(args.contains(&"--platform".to_string()));
2043        assert!(args.contains(&"linux/amd64".to_string()));
2044        assert!(args.contains(&"--read-only".to_string()));
2045        assert!(args.contains(&"--init".to_string()));
2046        assert!(args.contains(&"--rm".to_string()));
2047        assert!(args.contains(&"nginx:latest".to_string()));
2048
2049        // Image should be at the end (before any command args)
2050        let image_pos = args.iter().position(|x| x == "nginx:latest").unwrap();
2051        assert!(image_pos > 10); // Should be after all the options
2052    }
2053
2054    #[test]
2055    fn test_run_command_default_flag_values() {
2056        let cmd = RunCommand::new("alpine:latest");
2057        let args = cmd.build_args();
2058
2059        // Default flags should not appear in args unless explicitly changed
2060        assert!(!args.contains(&"--sig-proxy=false".to_string()));
2061        assert!(!args.contains(&"--disable-content-trust=false".to_string()));
2062        assert!(!args.contains(&"--read-only".to_string()));
2063        assert!(!args.contains(&"--privileged".to_string()));
2064        assert!(!args.contains(&"--init".to_string()));
2065    }
2066
2067    #[test]
2068    fn test_container_id() {
2069        let id = ContainerId("abcdef123456789".to_string());
2070        assert_eq!(id.as_str(), "abcdef123456789");
2071        assert_eq!(id.short(), "abcdef123456");
2072        assert_eq!(id.to_string(), "abcdef123456789");
2073
2074        let short_id = ContainerId("abc".to_string());
2075        assert_eq!(short_id.short(), "abc");
2076    }
2077
2078    #[test]
2079    fn test_it_convenience_method() {
2080        let cmd = RunCommand::new("alpine:latest").it();
2081        let args = cmd.build_args();
2082        assert!(args.contains(&"--interactive".to_string()));
2083        assert!(args.contains(&"--tty".to_string()));
2084    }
2085
2086    #[test]
2087    fn test_run_command_dns_network_options() {
2088        let cmd = RunCommand::new("alpine:latest")
2089            .dns("8.8.8.8")
2090            .dns("8.8.4.4")
2091            .dns_servers(vec!["1.1.1.1".to_string(), "1.0.0.1".to_string()])
2092            .dns_option("ndots:2")
2093            .dns_option("timeout:1")
2094            .dns_search("example.com")
2095            .dns_search("test.local")
2096            .add_host("api.example.com:127.0.0.1")
2097            .add_host("db.example.com:192.168.1.100");
2098
2099        let args = cmd.build_args();
2100
2101        // DNS servers
2102        assert!(args.contains(&"--dns".to_string()));
2103        assert!(args.contains(&"8.8.8.8".to_string()));
2104        assert!(args.contains(&"8.8.4.4".to_string()));
2105        assert!(args.contains(&"1.1.1.1".to_string()));
2106        assert!(args.contains(&"1.0.0.1".to_string()));
2107
2108        // DNS options
2109        assert!(args.contains(&"--dns-option".to_string()));
2110        assert!(args.contains(&"ndots:2".to_string()));
2111        assert!(args.contains(&"timeout:1".to_string()));
2112
2113        // DNS search domains
2114        assert!(args.contains(&"--dns-search".to_string()));
2115        assert!(args.contains(&"example.com".to_string()));
2116        assert!(args.contains(&"test.local".to_string()));
2117
2118        // Host mappings
2119        assert!(args.contains(&"--add-host".to_string()));
2120        assert!(args.contains(&"api.example.com:127.0.0.1".to_string()));
2121        assert!(args.contains(&"db.example.com:192.168.1.100".to_string()));
2122    }
2123
2124    #[test]
2125    fn test_run_command_security_capabilities() {
2126        let cmd = RunCommand::new("alpine:latest")
2127            .cap_add("NET_ADMIN")
2128            .cap_add("SYS_TIME")
2129            .cap_drop("CHOWN")
2130            .cap_drop("DAC_OVERRIDE")
2131            .security_opt("no-new-privileges:true")
2132            .security_opt("seccomp=unconfined");
2133
2134        let args = cmd.build_args();
2135
2136        // Capabilities to add
2137        assert!(args.contains(&"--cap-add".to_string()));
2138        assert!(args.contains(&"NET_ADMIN".to_string()));
2139        assert!(args.contains(&"SYS_TIME".to_string()));
2140
2141        // Capabilities to drop
2142        assert!(args.contains(&"--cap-drop".to_string()));
2143        assert!(args.contains(&"CHOWN".to_string()));
2144        assert!(args.contains(&"DAC_OVERRIDE".to_string()));
2145
2146        // Security options
2147        assert!(args.contains(&"--security-opt".to_string()));
2148        assert!(args.contains(&"no-new-privileges:true".to_string()));
2149        assert!(args.contains(&"seccomp=unconfined".to_string()));
2150    }
2151
2152    #[test]
2153    fn test_run_command_device_filesystem() {
2154        let cmd = RunCommand::new("alpine:latest")
2155            .device("/dev/sda:/dev/xvda:rwm")
2156            .device("/dev/zero")
2157            .tmpfs("/tmp:rw,size=100m")
2158            .tmpfs("/var/tmp:ro")
2159            .expose("80")
2160            .expose("443")
2161            .expose("8080/tcp");
2162
2163        let args = cmd.build_args();
2164
2165        // Devices
2166        assert!(args.contains(&"--device".to_string()));
2167        assert!(args.contains(&"/dev/sda:/dev/xvda:rwm".to_string()));
2168        assert!(args.contains(&"/dev/zero".to_string()));
2169
2170        // Tmpfs mounts
2171        assert!(args.contains(&"--tmpfs".to_string()));
2172        assert!(args.contains(&"/tmp:rw,size=100m".to_string()));
2173        assert!(args.contains(&"/var/tmp:ro".to_string()));
2174
2175        // Exposed ports
2176        assert!(args.contains(&"--expose".to_string()));
2177        assert!(args.contains(&"80".to_string()));
2178        assert!(args.contains(&"443".to_string()));
2179        assert!(args.contains(&"8080/tcp".to_string()));
2180    }
2181
2182    #[test]
2183    fn test_run_command_environment_labels() {
2184        use std::path::PathBuf;
2185
2186        let cmd = RunCommand::new("alpine:latest")
2187            .env_file(PathBuf::from("/etc/environment"))
2188            .env_file(PathBuf::from("./app.env"))
2189            .label("version=1.0.0")
2190            .label("maintainer=team@example.com")
2191            .label("app=myapp")
2192            .label_file(PathBuf::from("/etc/labels"))
2193            .label_file(PathBuf::from("./metadata.labels"));
2194
2195        let args = cmd.build_args();
2196
2197        // Environment files
2198        assert!(args.contains(&"--env-file".to_string()));
2199        assert!(args.contains(&"/etc/environment".to_string()));
2200        assert!(args.contains(&"./app.env".to_string()));
2201
2202        // Labels
2203        assert!(args.contains(&"--label".to_string()));
2204        assert!(args.contains(&"version=1.0.0".to_string()));
2205        assert!(args.contains(&"maintainer=team@example.com".to_string()));
2206        assert!(args.contains(&"app=myapp".to_string()));
2207
2208        // Label files
2209        assert!(args.contains(&"--label-file".to_string()));
2210        assert!(args.contains(&"/etc/labels".to_string()));
2211        assert!(args.contains(&"./metadata.labels".to_string()));
2212    }
2213
2214    #[test]
2215    fn test_run_command_all_high_impact_options() {
2216        use std::path::PathBuf;
2217
2218        let cmd = RunCommand::new("nginx:latest")
2219            .name("production-nginx")
2220            // DNS & Network
2221            .dns("8.8.8.8")
2222            .dns_option("ndots:2")
2223            .dns_search("example.com")
2224            .add_host("api.example.com:127.0.0.1")
2225            // Security & Capabilities
2226            .cap_add("NET_ADMIN")
2227            .cap_drop("CHOWN")
2228            .security_opt("no-new-privileges:true")
2229            // Device & Filesystem
2230            .device("/dev/null")
2231            .tmpfs("/tmp:rw,size=100m")
2232            .expose("80")
2233            // Environment & Labels
2234            .env_file(PathBuf::from(".env"))
2235            .label("version=1.0.0")
2236            .label_file(PathBuf::from("labels"));
2237
2238        let args = cmd.build_args();
2239
2240        // Verify all option types are present
2241        assert!(args.contains(&"--dns".to_string()));
2242        assert!(args.contains(&"--dns-option".to_string()));
2243        assert!(args.contains(&"--dns-search".to_string()));
2244        assert!(args.contains(&"--add-host".to_string()));
2245        assert!(args.contains(&"--cap-add".to_string()));
2246        assert!(args.contains(&"--cap-drop".to_string()));
2247        assert!(args.contains(&"--security-opt".to_string()));
2248        assert!(args.contains(&"--device".to_string()));
2249        assert!(args.contains(&"--tmpfs".to_string()));
2250        assert!(args.contains(&"--expose".to_string()));
2251        assert!(args.contains(&"--env-file".to_string()));
2252        assert!(args.contains(&"--label".to_string()));
2253        assert!(args.contains(&"--label-file".to_string()));
2254
2255        // Verify image is still at the end
2256        let image_pos = args.iter().position(|x| x == "nginx:latest").unwrap();
2257        assert!(image_pos > 0); // Should not be first
2258        assert!(image_pos < args.len() - 1 || args.len() == image_pos + 1); // Should be near end
2259    }
2260
2261    #[test]
2262    fn test_run_command_empty_lists_not_added() {
2263        let cmd = RunCommand::new("alpine:latest");
2264        let args = cmd.build_args();
2265
2266        // Ensure empty lists don't add any arguments
2267        assert!(!args.contains(&"--dns".to_string()));
2268        assert!(!args.contains(&"--dns-option".to_string()));
2269        assert!(!args.contains(&"--dns-search".to_string()));
2270        assert!(!args.contains(&"--add-host".to_string()));
2271        assert!(!args.contains(&"--cap-add".to_string()));
2272        assert!(!args.contains(&"--cap-drop".to_string()));
2273        assert!(!args.contains(&"--security-opt".to_string()));
2274        assert!(!args.contains(&"--device".to_string()));
2275        assert!(!args.contains(&"--tmpfs".to_string()));
2276        assert!(!args.contains(&"--expose".to_string()));
2277        assert!(!args.contains(&"--env-file".to_string()));
2278        assert!(!args.contains(&"--label".to_string()));
2279        assert!(!args.contains(&"--label-file".to_string()));
2280
2281        // Additional list options should also not be present
2282        assert!(!args.contains(&"--network-alias".to_string()));
2283        assert!(!args.contains(&"--group-add".to_string()));
2284        assert!(!args.contains(&"--attach".to_string()));
2285        assert!(!args.contains(&"--log-opt".to_string()));
2286        assert!(!args.contains(&"--storage-opt".to_string()));
2287        assert!(!args.contains(&"--ulimit".to_string()));
2288        assert!(!args.contains(&"--volumes-from".to_string()));
2289        assert!(!args.contains(&"--link".to_string()));
2290        assert!(!args.contains(&"--link-local-ip".to_string()));
2291
2292        // But image should still be there
2293        assert!(args.contains(&"alpine:latest".to_string()));
2294    }
2295
2296    #[test]
2297    fn test_run_command_additional_list_options() {
2298        let cmd = RunCommand::new("alpine:latest")
2299            .network_alias("web")
2300            .network_alias("frontend")
2301            .group_add("staff")
2302            .group_add("docker")
2303            .attach("stdout")
2304            .attach("stderr")
2305            .log_opt("max-size=10m")
2306            .log_opt("max-file=3")
2307            .storage_opt("size=20G")
2308            .ulimit("nofile=1024:65536")
2309            .ulimit("nproc=1024")
2310            .volumes_from("data-container")
2311            .volumes_from("config-container:ro")
2312            .link("db:database")
2313            .link("cache:redis")
2314            .link_local_ip("169.254.1.1")
2315            .link_local_ip("fe80::1");
2316
2317        let args = cmd.build_args();
2318
2319        // Network aliases
2320        assert!(args.contains(&"--network-alias".to_string()));
2321        assert!(args.contains(&"web".to_string()));
2322        assert!(args.contains(&"frontend".to_string()));
2323
2324        // Group additions
2325        assert!(args.contains(&"--group-add".to_string()));
2326        assert!(args.contains(&"staff".to_string()));
2327        assert!(args.contains(&"docker".to_string()));
2328
2329        // Stream attachments
2330        assert!(args.contains(&"--attach".to_string()));
2331        assert!(args.contains(&"stdout".to_string()));
2332        assert!(args.contains(&"stderr".to_string()));
2333
2334        // Log options
2335        assert!(args.contains(&"--log-opt".to_string()));
2336        assert!(args.contains(&"max-size=10m".to_string()));
2337        assert!(args.contains(&"max-file=3".to_string()));
2338
2339        // Storage options
2340        assert!(args.contains(&"--storage-opt".to_string()));
2341        assert!(args.contains(&"size=20G".to_string()));
2342
2343        // Ulimits
2344        assert!(args.contains(&"--ulimit".to_string()));
2345        assert!(args.contains(&"nofile=1024:65536".to_string()));
2346        assert!(args.contains(&"nproc=1024".to_string()));
2347
2348        // Volumes from containers
2349        assert!(args.contains(&"--volumes-from".to_string()));
2350        assert!(args.contains(&"data-container".to_string()));
2351        assert!(args.contains(&"config-container:ro".to_string()));
2352
2353        // Container links
2354        assert!(args.contains(&"--link".to_string()));
2355        assert!(args.contains(&"db:database".to_string()));
2356        assert!(args.contains(&"cache:redis".to_string()));
2357
2358        // Link-local IPs
2359        assert!(args.contains(&"--link-local-ip".to_string()));
2360        assert!(args.contains(&"169.254.1.1".to_string()));
2361        assert!(args.contains(&"fe80::1".to_string()));
2362    }
2363
2364    #[test]
2365    fn test_run_command_additional_list_individual_options() {
2366        // Test each additional list option individually for proper argument generation
2367        let network_cmd = RunCommand::new("alpine:latest").network_alias("api");
2368        let network_args = network_cmd.build_args();
2369        assert!(network_args.contains(&"--network-alias".to_string()));
2370        assert!(network_args.contains(&"api".to_string()));
2371
2372        let group_cmd = RunCommand::new("alpine:latest").group_add("wheel");
2373        let group_args = group_cmd.build_args();
2374        assert!(group_args.contains(&"--group-add".to_string()));
2375        assert!(group_args.contains(&"wheel".to_string()));
2376
2377        let attach_cmd = RunCommand::new("alpine:latest").attach("stdin");
2378        let attach_args = attach_cmd.build_args();
2379        assert!(attach_args.contains(&"--attach".to_string()));
2380        assert!(attach_args.contains(&"stdin".to_string()));
2381
2382        let log_cmd = RunCommand::new("alpine:latest").log_opt("compress=true");
2383        let log_args = log_cmd.build_args();
2384        assert!(log_args.contains(&"--log-opt".to_string()));
2385        assert!(log_args.contains(&"compress=true".to_string()));
2386
2387        let storage_cmd =
2388            RunCommand::new("alpine:latest").storage_opt("dm.thinpooldev=/dev/mapper/thin-pool");
2389        let storage_args = storage_cmd.build_args();
2390        assert!(storage_args.contains(&"--storage-opt".to_string()));
2391        assert!(storage_args.contains(&"dm.thinpooldev=/dev/mapper/thin-pool".to_string()));
2392
2393        let ulimit_cmd = RunCommand::new("alpine:latest").ulimit("memlock=-1:-1");
2394        let ulimit_args = ulimit_cmd.build_args();
2395        assert!(ulimit_args.contains(&"--ulimit".to_string()));
2396        assert!(ulimit_args.contains(&"memlock=-1:-1".to_string()));
2397
2398        let volumes_cmd = RunCommand::new("alpine:latest").volumes_from("shared-data");
2399        let volumes_args = volumes_cmd.build_args();
2400        assert!(volumes_args.contains(&"--volumes-from".to_string()));
2401        assert!(volumes_args.contains(&"shared-data".to_string()));
2402
2403        let link_cmd = RunCommand::new("alpine:latest").link("mysql:db");
2404        let link_args = link_cmd.build_args();
2405        assert!(link_args.contains(&"--link".to_string()));
2406        assert!(link_args.contains(&"mysql:db".to_string()));
2407
2408        let ip_cmd = RunCommand::new("alpine:latest").link_local_ip("169.254.100.1");
2409        let ip_args = ip_cmd.build_args();
2410        assert!(ip_args.contains(&"--link-local-ip".to_string()));
2411        assert!(ip_args.contains(&"169.254.100.1".to_string()));
2412    }
2413
2414    #[test]
2415    fn test_run_command_health_check_options() {
2416        let cmd = RunCommand::new("nginx:latest")
2417            .health_cmd("curl -f http://localhost/ || exit 1")
2418            .health_interval("30s")
2419            .health_retries(3)
2420            .health_timeout("5s")
2421            .health_start_period("60s")
2422            .health_start_interval("5s");
2423
2424        let args = cmd.build_args();
2425
2426        // Health check options
2427        assert!(args.contains(&"--health-cmd".to_string()));
2428        assert!(args.contains(&"curl -f http://localhost/ || exit 1".to_string()));
2429        assert!(args.contains(&"--health-interval".to_string()));
2430        assert!(args.contains(&"30s".to_string()));
2431        assert!(args.contains(&"--health-retries".to_string()));
2432        assert!(args.contains(&"3".to_string()));
2433        assert!(args.contains(&"--health-timeout".to_string()));
2434        assert!(args.contains(&"5s".to_string()));
2435        assert!(args.contains(&"--health-start-period".to_string()));
2436        assert!(args.contains(&"60s".to_string()));
2437        assert!(args.contains(&"--health-start-interval".to_string()));
2438        // Note: "5s" appears twice, so we can't easily test for this specific one
2439    }
2440
2441    #[test]
2442    fn test_run_command_advanced_mount_network_options() {
2443        let cmd = RunCommand::new("alpine:latest")
2444            .mount("type=bind,source=/host/path,target=/container/path")
2445            .mount("type=volume,source=data-vol,target=/data")
2446            .network("frontend")
2447            .network("backend")
2448            .gpus("all")
2449            .annotation("io.kubernetes.cri-o.Devices", "/dev/fuse")
2450            .annotation("io.kubernetes.cri-o.ShmSize", "64m")
2451            .sysctl("net.core.somaxconn", "1024")
2452            .sysctl("kernel.shm_rmid_forced", "1");
2453
2454        let args = cmd.build_args();
2455
2456        // Mount options
2457        assert!(args.contains(&"--mount".to_string()));
2458        assert!(args.contains(&"type=bind,source=/host/path,target=/container/path".to_string()));
2459        assert!(args.contains(&"type=volume,source=data-vol,target=/data".to_string()));
2460
2461        // Network options
2462        assert!(args.contains(&"--network".to_string()));
2463        assert!(args.contains(&"frontend".to_string()));
2464        assert!(args.contains(&"backend".to_string()));
2465
2466        // GPU options
2467        assert!(args.contains(&"--gpus".to_string()));
2468        assert!(args.contains(&"all".to_string()));
2469
2470        // Annotation options
2471        assert!(args.contains(&"--annotation".to_string()));
2472        assert!(args.contains(&"io.kubernetes.cri-o.Devices=/dev/fuse".to_string()));
2473        assert!(args.contains(&"io.kubernetes.cri-o.ShmSize=64m".to_string()));
2474
2475        // Sysctl options
2476        assert!(args.contains(&"--sysctl".to_string()));
2477        assert!(args.contains(&"net.core.somaxconn=1024".to_string()));
2478        assert!(args.contains(&"kernel.shm_rmid_forced=1".to_string()));
2479    }
2480
2481    #[test]
2482    fn test_run_command_health_advanced_individual_options() {
2483        // Test each health check and advanced option individually
2484        let health_cmd = RunCommand::new("alpine:latest").health_cmd("ping -c 1 localhost");
2485        let health_args = health_cmd.build_args();
2486        assert!(health_args.contains(&"--health-cmd".to_string()));
2487        assert!(health_args.contains(&"ping -c 1 localhost".to_string()));
2488
2489        let health_interval = RunCommand::new("alpine:latest").health_interval("10s");
2490        let interval_args = health_interval.build_args();
2491        assert!(interval_args.contains(&"--health-interval".to_string()));
2492        assert!(interval_args.contains(&"10s".to_string()));
2493
2494        let health_retries = RunCommand::new("alpine:latest").health_retries(5);
2495        let retries_args = health_retries.build_args();
2496        assert!(retries_args.contains(&"--health-retries".to_string()));
2497        assert!(retries_args.contains(&"5".to_string()));
2498
2499        let mount_cmd = RunCommand::new("alpine:latest").mount("type=tmpfs,destination=/app");
2500        let mount_args = mount_cmd.build_args();
2501        assert!(mount_args.contains(&"--mount".to_string()));
2502        assert!(mount_args.contains(&"type=tmpfs,destination=/app".to_string()));
2503
2504        let network_cmd = RunCommand::new("alpine:latest").network("my-network");
2505        let network_args = network_cmd.build_args();
2506        assert!(network_args.contains(&"--network".to_string()));
2507        assert!(network_args.contains(&"my-network".to_string()));
2508
2509        let gpu_cmd = RunCommand::new("alpine:latest").gpus("device=0");
2510        let gpu_args = gpu_cmd.build_args();
2511        assert!(gpu_args.contains(&"--gpus".to_string()));
2512        assert!(gpu_args.contains(&"device=0".to_string()));
2513
2514        let annotation_cmd = RunCommand::new("alpine:latest").annotation("key", "value");
2515        let annotation_args = annotation_cmd.build_args();
2516        assert!(annotation_args.contains(&"--annotation".to_string()));
2517        assert!(annotation_args.contains(&"key=value".to_string()));
2518
2519        let sysctl_cmd = RunCommand::new("alpine:latest").sysctl("net.ipv4.ip_forward", "1");
2520        let sysctl_args = sysctl_cmd.build_args();
2521        assert!(sysctl_args.contains(&"--sysctl".to_string()));
2522        assert!(sysctl_args.contains(&"net.ipv4.ip_forward=1".to_string()));
2523    }
2524
2525    #[test]
2526    fn test_run_command_comprehensive_health_advanced_integration() {
2527        let cmd = RunCommand::new("web-app:latest")
2528            .name("production-web-app")
2529            // Health checks for production readiness
2530            .health_cmd("curl -f http://localhost:8080/health || exit 1")
2531            .health_interval("30s")
2532            .health_retries(3)
2533            .health_timeout("10s")
2534            .health_start_period("120s")
2535            // Advanced mounting and networking
2536            .mount("type=bind,source=/var/log/app,target=/app/logs")
2537            .mount("type=volume,source=app-data,target=/app/data")
2538            .network("frontend")
2539            .network("backend")
2540            // GPU support for ML workloads
2541            .gpus("device=0,1")
2542            // Kubernetes annotations
2543            .annotation(
2544                "io.kubernetes.container.apparmor.security.beta.kubernetes.io/app",
2545                "runtime/default",
2546            )
2547            .annotation(
2548                "io.kubernetes.container.seccomp.security.alpha.kubernetes.io/app",
2549                "runtime/default",
2550            )
2551            // System tuning
2552            .sysctl("net.core.somaxconn", "65535")
2553            .sysctl("net.ipv4.tcp_keepalive_time", "600")
2554            // Additional standard options
2555            .port(8080, 8080)
2556            .env("NODE_ENV", "production")
2557            .memory("2g")
2558            .cpus("2.0")
2559            .restart("unless-stopped")
2560            .detach();
2561
2562        let args = cmd.build_args();
2563
2564        // Verify all health check and advanced options are present
2565        assert!(args.contains(&"--health-cmd".to_string()));
2566        assert!(args.contains(&"--health-interval".to_string()));
2567        assert!(args.contains(&"--health-retries".to_string()));
2568        assert!(args.contains(&"--health-timeout".to_string()));
2569        assert!(args.contains(&"--health-start-period".to_string()));
2570        assert!(args.contains(&"--mount".to_string()));
2571        assert!(args.contains(&"--network".to_string()));
2572        assert!(args.contains(&"--gpus".to_string()));
2573        assert!(args.contains(&"--annotation".to_string()));
2574        assert!(args.contains(&"--sysctl".to_string()));
2575
2576        // Verify image is still at the end
2577        let image_pos = args.iter().position(|x| x == "web-app:latest").unwrap();
2578        assert!(image_pos > 0);
2579    }
2580
2581    #[test]
2582    fn test_run_command_block_io_controls() {
2583        let cmd = RunCommand::new("alpine:latest")
2584            .blkio_weight(500)
2585            .blkio_weight_device("/dev/sda:300")
2586            .blkio_weight_device("/dev/sdb:700")
2587            .device_read_bps("/dev/sda:50mb")
2588            .device_write_bps("/dev/sda:30mb")
2589            .device_read_iops("/dev/sda:1000")
2590            .device_write_iops("/dev/sda:800");
2591
2592        let args = cmd.build_args();
2593
2594        // Block I/O weight
2595        assert!(args.contains(&"--blkio-weight".to_string()));
2596        assert!(args.contains(&"500".to_string()));
2597
2598        // Block I/O weight per device
2599        assert!(args.contains(&"--blkio-weight-device".to_string()));
2600        assert!(args.contains(&"/dev/sda:300".to_string()));
2601        assert!(args.contains(&"/dev/sdb:700".to_string()));
2602
2603        // Device read/write BPS
2604        assert!(args.contains(&"--device-read-bps".to_string()));
2605        assert!(args.contains(&"/dev/sda:50mb".to_string()));
2606        assert!(args.contains(&"--device-write-bps".to_string()));
2607        assert!(args.contains(&"/dev/sda:30mb".to_string()));
2608
2609        // Device read/write IOPS
2610        assert!(args.contains(&"--device-read-iops".to_string()));
2611        assert!(args.contains(&"/dev/sda:1000".to_string()));
2612        assert!(args.contains(&"--device-write-iops".to_string()));
2613        assert!(args.contains(&"/dev/sda:800".to_string()));
2614    }
2615
2616    #[test]
2617    fn test_run_command_realtime_cpu_networking() {
2618        let cmd = RunCommand::new("alpine:latest")
2619            .cpu_rt_period(1_000_000)
2620            .cpu_rt_runtime(950_000)
2621            .ip("172.30.100.104")
2622            .ip6("2001:db8::33")
2623            .device_cgroup_rule("c 1:3 mr")
2624            .device_cgroup_rule("a 7:* rmw");
2625
2626        let args = cmd.build_args();
2627
2628        // Real-time CPU scheduling
2629        assert!(args.contains(&"--cpu-rt-period".to_string()));
2630        assert!(args.contains(&"1000000".to_string()));
2631        assert!(args.contains(&"--cpu-rt-runtime".to_string()));
2632        assert!(args.contains(&"950000".to_string()));
2633
2634        // Advanced networking
2635        assert!(args.contains(&"--ip".to_string()));
2636        assert!(args.contains(&"172.30.100.104".to_string()));
2637        assert!(args.contains(&"--ip6".to_string()));
2638        assert!(args.contains(&"2001:db8::33".to_string()));
2639
2640        // Device cgroup rules
2641        assert!(args.contains(&"--device-cgroup-rule".to_string()));
2642        assert!(args.contains(&"c 1:3 mr".to_string()));
2643        assert!(args.contains(&"a 7:* rmw".to_string()));
2644    }
2645
2646    #[test]
2647    fn test_run_command_advanced_system_individual_options() {
2648        // Test each advanced system option individually
2649        let blkio_cmd = RunCommand::new("alpine:latest").blkio_weight(100);
2650        let blkio_args = blkio_cmd.build_args();
2651        assert!(blkio_args.contains(&"--blkio-weight".to_string()));
2652        assert!(blkio_args.contains(&"100".to_string()));
2653
2654        let weight_device_cmd =
2655            RunCommand::new("alpine:latest").blkio_weight_device("/dev/sda:500");
2656        let weight_device_args = weight_device_cmd.build_args();
2657        assert!(weight_device_args.contains(&"--blkio-weight-device".to_string()));
2658        assert!(weight_device_args.contains(&"/dev/sda:500".to_string()));
2659
2660        let read_bps_cmd = RunCommand::new("alpine:latest").device_read_bps("/dev/sda:1mb");
2661        let read_bps_args = read_bps_cmd.build_args();
2662        assert!(read_bps_args.contains(&"--device-read-bps".to_string()));
2663        assert!(read_bps_args.contains(&"/dev/sda:1mb".to_string()));
2664
2665        let write_bps_cmd = RunCommand::new("alpine:latest").device_write_bps("/dev/sda:1mb");
2666        let write_bps_args = write_bps_cmd.build_args();
2667        assert!(write_bps_args.contains(&"--device-write-bps".to_string()));
2668        assert!(write_bps_args.contains(&"/dev/sda:1mb".to_string()));
2669
2670        let read_iops_cmd = RunCommand::new("alpine:latest").device_read_iops("/dev/sda:100");
2671        let read_iops_args = read_iops_cmd.build_args();
2672        assert!(read_iops_args.contains(&"--device-read-iops".to_string()));
2673        assert!(read_iops_args.contains(&"/dev/sda:100".to_string()));
2674
2675        let write_iops_cmd = RunCommand::new("alpine:latest").device_write_iops("/dev/sda:100");
2676        let write_iops_args = write_iops_cmd.build_args();
2677        assert!(write_iops_args.contains(&"--device-write-iops".to_string()));
2678        assert!(write_iops_args.contains(&"/dev/sda:100".to_string()));
2679
2680        let rt_period_cmd = RunCommand::new("alpine:latest").cpu_rt_period(100_000);
2681        let rt_period_args = rt_period_cmd.build_args();
2682        assert!(rt_period_args.contains(&"--cpu-rt-period".to_string()));
2683        assert!(rt_period_args.contains(&"100000".to_string()));
2684
2685        let rt_runtime_cmd = RunCommand::new("alpine:latest").cpu_rt_runtime(95_000);
2686        let rt_runtime_args = rt_runtime_cmd.build_args();
2687        assert!(rt_runtime_args.contains(&"--cpu-rt-runtime".to_string()));
2688        assert!(rt_runtime_args.contains(&"95000".to_string()));
2689
2690        let ip_cmd = RunCommand::new("alpine:latest").ip("192.168.1.100");
2691        let ip_args = ip_cmd.build_args();
2692        assert!(ip_args.contains(&"--ip".to_string()));
2693        assert!(ip_args.contains(&"192.168.1.100".to_string()));
2694
2695        let ipv6_cmd = RunCommand::new("alpine:latest").ip6("fe80::1");
2696        let ipv6_args = ipv6_cmd.build_args();
2697        assert!(ipv6_args.contains(&"--ip6".to_string()));
2698        assert!(ipv6_args.contains(&"fe80::1".to_string()));
2699
2700        let cgroup_rule_cmd = RunCommand::new("alpine:latest").device_cgroup_rule("c 1:1 rwm");
2701        let cgroup_rule_args = cgroup_rule_cmd.build_args();
2702        assert!(cgroup_rule_args.contains(&"--device-cgroup-rule".to_string()));
2703        assert!(cgroup_rule_args.contains(&"c 1:1 rwm".to_string()));
2704    }
2705
2706    #[test]
2707    #[allow(clippy::too_many_lines)]
2708    fn test_run_command_complete_100_percent_coverage() {
2709        use std::path::PathBuf;
2710
2711        // Test demonstrating ALL 96 Docker run options are now implemented
2712        let cmd = RunCommand::new("enterprise-app:latest")
2713            .name("production-enterprise")
2714            // Basic options
2715            .detach()
2716            .interactive()
2717            .tty()
2718            .remove()
2719            // Environment and ports
2720            .env("NODE_ENV", "production")
2721            .port(8080, 8080)
2722            .volume("/data", "/app/data")
2723            .workdir("/app")
2724            .entrypoint("/app/start.sh")
2725            // Resource limits (Phase 2)
2726            .memory("4g")
2727            .cpus("2.0")
2728            .cpu_shares(1024)
2729            .cpu_period(100_000)
2730            .cpu_quota(50000)
2731            .cpuset_cpus("0-1")
2732            .cpuset_mems("0")
2733            .memory_swap("8g")
2734            .memory_reservation("2g")
2735            // Security & User Context (Phase 2)
2736            .user("app:app")
2737            .privileged()
2738            .hostname("enterprise-app")
2739            // Lifecycle Management (Phase 2)
2740            .restart("unless-stopped")
2741            // System Integration (Phase 2)
2742            .platform("linux/amd64")
2743            .runtime("runc")
2744            .isolation("default")
2745            .pull("always")
2746            .cidfile("/tmp/container.cid")
2747            .domainname("enterprise.local")
2748            .mac_address("02:42:ac:11:00:02")
2749            // Logging & Drivers (Phase 2)
2750            .log_driver("json-file")
2751            .volume_driver("local")
2752            // Namespaces (Phase 2)
2753            .userns("host")
2754            .uts("host")
2755            .pid("host")
2756            .ipc("host")
2757            .cgroupns("host")
2758            .cgroup_parent("/docker")
2759            // Advanced Memory & Performance (Phase 2)
2760            .kernel_memory("1g")
2761            .memory_swappiness(10)
2762            .oom_score_adj(-500)
2763            .pids_limit(1000)
2764            .shm_size("64m")
2765            // Process Control (Phase 2)
2766            .stop_signal("SIGTERM")
2767            .stop_timeout(30)
2768            .detach_keys("ctrl-p,ctrl-q")
2769            // Simple Flags (Phase 2)
2770            .no_sig_proxy()
2771            .read_only()
2772            .init()
2773            .oom_kill_disable()
2774            .no_healthcheck()
2775            .enable_content_trust()
2776            .publish_all()
2777            .quiet()
2778            // High-Impact List Options (Phase 3)
2779            .dns("8.8.8.8")
2780            .dns_option("ndots:2")
2781            .dns_search("enterprise.local")
2782            .add_host("api.enterprise.local:10.0.1.100")
2783            .cap_add("NET_ADMIN")
2784            .cap_drop("ALL")
2785            .security_opt("no-new-privileges:true")
2786            .device("/dev/null")
2787            .tmpfs("/tmp:size=100m")
2788            .expose("9090")
2789            .env_file(PathBuf::from(".env.production"))
2790            .label("app=enterprise")
2791            .label_file(PathBuf::from("labels.txt"))
2792            // Batch 1: Additional List/Vec Options
2793            .network_alias("enterprise-primary")
2794            .group_add("staff")
2795            .attach("stdout")
2796            .log_opt("max-size=10m")
2797            .storage_opt("size=100G")
2798            .ulimit("nofile=65536:65536")
2799            .volumes_from("data-container")
2800            .link("db:database")
2801            .link_local_ip("169.254.1.1")
2802            // Batch 2: Medium Complexity Options
2803            .health_cmd("curl -f http://localhost:8080/health || exit 1")
2804            .health_interval("30s")
2805            .health_retries(3)
2806            .health_timeout("10s")
2807            .health_start_period("60s")
2808            .health_start_interval("5s")
2809            .mount("type=bind,source=/host/config,target=/app/config")
2810            .network("enterprise-net")
2811            .gpus("device=0")
2812            .annotation("io.kubernetes.cri-o.TTY", "true")
2813            .sysctl("net.core.somaxconn", "65535")
2814            // Batch 3: Complex/Advanced Options
2815            .blkio_weight(500)
2816            .blkio_weight_device("/dev/sda:300")
2817            .device_read_bps("/dev/sda:100mb")
2818            .device_write_bps("/dev/sda:50mb")
2819            .device_read_iops("/dev/sda:1000")
2820            .device_write_iops("/dev/sda:500")
2821            .cpu_rt_period(1_000_000)
2822            .cpu_rt_runtime(950_000)
2823            .ip("10.0.1.50")
2824            .ip6("2001:db8::50")
2825            .device_cgroup_rule("c 1:1 rwm");
2826
2827        let args = cmd.build_args();
2828
2829        // Verify we have a substantial command with all option types
2830        assert!(args.len() > 150); // Should be a very long command
2831
2832        // Verify key options from each batch are present
2833        assert!(args.contains(&"--detach".to_string()));
2834        assert!(args.contains(&"--memory".to_string()));
2835        assert!(args.contains(&"--dns".to_string()));
2836        assert!(args.contains(&"--network-alias".to_string()));
2837        assert!(args.contains(&"--health-cmd".to_string()));
2838        assert!(args.contains(&"--blkio-weight".to_string()));
2839
2840        // Verify image is still at the end
2841        let image_pos = args
2842            .iter()
2843            .position(|x| x == "enterprise-app:latest")
2844            .unwrap();
2845        assert!(image_pos > 100); // Should be very far into the command
2846        assert_eq!(args[args.len() - 1], "enterprise-app:latest");
2847
2848        println!("🎉 COMPLETE! All 96 Docker run options implemented and tested!");
2849    }
2850}