docker_wrapper/
lib.rs

1//! # docker-wrapper
2//!
3//! A comprehensive, type-safe Docker CLI wrapper for Rust applications.
4//!
5//! `docker-wrapper` provides a clean, idiomatic Rust interface to Docker's command-line interface,
6//! supporting all major Docker commands with strong typing, async/await support, and a consistent
7//! builder pattern API.
8//!
9//! ## Features
10//!
11//! - **Complete Docker CLI coverage**: Implements all 35 essential Docker commands
12//! - **Type-safe builder pattern**: Compile-time validation of command construction
13//! - **Async/await support**: Built on Tokio for efficient async operations
14//! - **Streaming support**: Real-time output streaming for long-running commands
15//! - **Docker Compose support**: Optional feature for multi-container orchestration
16//! - **Zero dependencies on Docker SDK**: Works directly with the Docker CLI
17//! - **Comprehensive error handling**: Detailed error messages and types
18//! - **Well-tested**: Extensive unit and integration test coverage
19//!
20//! ## Quick Start
21//!
22//! Add `docker-wrapper` to your `Cargo.toml`:
23//!
24//! ```toml
25//! [dependencies]
26//! docker-wrapper = "0.2"
27//! tokio = { version = "1", features = ["full"] }
28//! ```
29//!
30//! For Docker Compose support, enable the `compose` feature:
31//!
32//! ```toml
33//! [dependencies]
34//! docker-wrapper = { version = "0.2", features = ["compose"] }
35//! ```
36//!
37//! ## Basic Usage
38//!
39//! ### Running a Container
40//!
41//! ```rust,no_run
42//! use docker_wrapper::{DockerCommand, RunCommand};
43//!
44//! # #[tokio::main]
45//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
46//! // Run a simple container
47//! let output = RunCommand::new("nginx:latest")
48//!     .name("my-web-server")
49//!     .port(8080, 80)
50//!     .detach()
51//!     .execute()
52//!     .await?;
53//!
54//! println!("Container started: {}", output.0);
55//! # Ok(())
56//! # }
57//! ```
58//!
59//! ### Building an Image
60//!
61//! ```rust,no_run
62//! use docker_wrapper::{DockerCommand, BuildCommand};
63//!
64//! # #[tokio::main]
65//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
66//! let output = BuildCommand::new(".")
67//!     .tag("my-app:latest")
68//!     .file("Dockerfile")
69//!     .build_arg("VERSION", "1.0.0")
70//!     .execute()
71//!     .await?;
72//!
73//! if let Some(image_id) = &output.image_id {
74//!     println!("Built image: {}", image_id);
75//! }
76//! # Ok(())
77//! # }
78//! ```
79//!
80//! ### Listing Containers
81//!
82//! ```rust,no_run
83//! use docker_wrapper::{DockerCommand, PsCommand};
84//!
85//! # #[tokio::main]
86//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
87//! let output = PsCommand::new()
88//!     .all()
89//!     .format_json()
90//!     .execute()
91//!     .await?;
92//!
93//! for container in output.containers {
94//!     println!("{}: {}", container.names, container.status);
95//! }
96//! # Ok(())
97//! # }
98//! ```
99//!
100//! ## Streaming Output
101//!
102//! For long-running commands, use the streaming API to process output in real-time:
103//!
104//! ```rust,no_run
105//! use docker_wrapper::{BuildCommand, StreamHandler, StreamableCommand};
106//!
107//! # #[tokio::main]
108//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
109//! // Stream build output to console
110//! let result = BuildCommand::new(".")
111//!     .tag("my-app:latest")
112//!     .stream(StreamHandler::print())
113//!     .await?;
114//!
115//! if result.is_success() {
116//!     println!("Build completed successfully!");
117//! }
118//! # Ok(())
119//! # }
120//! ```
121//!
122//! ## Docker Compose Support
123//!
124//! When the `compose` feature is enabled, you can manage multi-container applications:
125//!
126//! ```rust,no_run
127//! # #[cfg(feature = "compose")]
128//! use docker_wrapper::compose::{ComposeCommand, ComposeUpCommand, ComposeDownCommand};
129//!
130//! # #[cfg(feature = "compose")]
131//! # #[tokio::main]
132//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
133//! // Start services defined in docker-compose.yml
134//! ComposeUpCommand::new()
135//!     .file("docker-compose.yml")
136//!     .detach()
137//!     .execute()
138//!     .await?;
139//!
140//! // Stop and remove services
141//! ComposeDownCommand::new()
142//!     .volumes()
143//!     .execute()
144//!     .await?;
145//! # Ok(())
146//! # }
147//! # #[cfg(not(feature = "compose"))]
148//! # fn main() {}
149//! ```
150//!
151//! ## Architecture
152//!
153//! The crate is organized around several key design patterns:
154//!
155//! ### Command Trait Pattern
156//!
157//! All Docker commands implement the `DockerCommand` trait, providing a consistent interface:
158//!
159//! - `new()` - Create a new command instance
160//! - `execute()` - Run the command and return typed output
161//! - Builder methods for setting options
162//!
163//! ### Builder Pattern
164//!
165//! Commands use the builder pattern for configuration, allowing fluent and intuitive API usage:
166//!
167//! ```rust,no_run
168//! # use docker_wrapper::{DockerCommand, RunCommand};
169//! # #[tokio::main]
170//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
171//! RunCommand::new("alpine")
172//!     .name("my-container")
173//!     .env("KEY", "value")
174//!     .volume("/host/path", "/container/path")
175//!     .workdir("/app")
176//!     .cmd(vec!["echo".to_string(), "hello".to_string()])
177//!     .execute()
178//!     .await?;
179//! # Ok(())
180//! # }
181//! ```
182//!
183//! ### Error Handling
184//!
185//! All operations return `Result<T, docker_wrapper::Error>`, providing detailed error information:
186//!
187//! ```rust,no_run
188//! # use docker_wrapper::{DockerCommand, RunCommand};
189//! # #[tokio::main]
190//! # async fn main() {
191//! match RunCommand::new("invalid:image").execute().await {
192//!     Ok(output) => println!("Container ID: {}", output.0),
193//!     Err(e) => eprintln!("Failed to run container: {}", e),
194//! }
195//! # }
196//! ```
197//!
198//! ## Command Coverage
199//!
200//! ### Container Commands
201//! - `run` - Run a new container
202//! - `exec` - Execute commands in running containers
203//! - `ps` - List containers
204//! - `create` - Create a new container without starting it
205//! - `start` - Start stopped containers
206//! - `stop` - Stop running containers
207//! - `restart` - Restart containers
208//! - `kill` - Kill running containers
209//! - `rm` - Remove containers
210//! - `pause` - Pause running containers
211//! - `unpause` - Unpause paused containers
212//! - `attach` - Attach to running containers
213//! - `wait` - Wait for containers to stop
214//! - `logs` - Fetch container logs
215//! - `top` - Display running processes in containers
216//! - `stats` - Display resource usage statistics
217//! - `port` - List port mappings
218//! - `rename` - Rename containers
219//! - `update` - Update container configuration
220//! - `cp` - Copy files between containers and host
221//! - `diff` - Inspect filesystem changes
222//! - `export` - Export container filesystem
223//! - `commit` - Create image from container
224//!
225//! ### Image Commands
226//! - `images` - List images
227//! - `pull` - Pull images from registry
228//! - `push` - Push images to registry
229//! - `build` - Build images from Dockerfile
230//! - `load` - Load images from tar archive
231//! - `save` - Save images to tar archive
232//! - `rmi` - Remove images
233//! - `tag` - Tag images
234//! - `import` - Import images from tarball
235//! - `history` - Show image history
236//! - `inspect` - Display detailed information
237//! - `search` - Search Docker Hub for images
238//!
239//! ### System Commands
240//! - `info` - Display system information
241//! - `version` - Show Docker version
242//! - `events` - Monitor Docker events
243//! - `login` - Log in to registry
244//! - `logout` - Log out from registry
245//!
246//! ## Prerequisites Check
247//!
248//! Ensure Docker is installed and accessible:
249//!
250//! ```rust,no_run
251//! use docker_wrapper::ensure_docker;
252//!
253//! # #[tokio::main]
254//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
255//! // Check Docker availability and version
256//! let docker_info = ensure_docker().await?;
257//! println!("Docker version: {}.{}.{}",
258//!     docker_info.version.major,
259//!     docker_info.version.minor,
260//!     docker_info.version.patch);
261//! # Ok(())
262//! # }
263//! ```
264//!
265//! ## Best Practices
266//!
267//! ### Resource Cleanup
268//!
269//! Always clean up containers and resources:
270//!
271//! ```rust,no_run
272//! # use docker_wrapper::{DockerCommand, RunCommand, RmCommand};
273//! # #[tokio::main]
274//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
275//! // Use auto-remove for temporary containers
276//! RunCommand::new("alpine")
277//!     .remove()  // Automatically remove when stopped
278//!     .execute()
279//!     .await?;
280//!
281//! // Or manually remove containers
282//! RmCommand::new("my-container")
283//!     .force()
284//!     .execute()
285//!     .await?;
286//! # Ok(())
287//! # }
288//! ```
289//!
290//! ### Error Handling
291//!
292//! Handle errors appropriately for production use:
293//!
294//! ```rust,no_run
295//! # use docker_wrapper::{DockerCommand, RunCommand, Error};
296//! # #[tokio::main]
297//! # async fn main() {
298//! async fn run_container() -> Result<String, Error> {
299//!     let output = RunCommand::new("nginx")
300//!         .detach()
301//!         .execute()
302//!         .await?;
303//!     Ok(output.0)
304//! }
305//!
306//! match run_container().await {
307//!     Ok(id) => println!("Started container: {}", id),
308//!     Err(Error::CommandFailed { stderr, .. }) => {
309//!         eprintln!("Docker command failed: {}", stderr);
310//!     }
311//!     Err(e) => eprintln!("Unexpected error: {}", e),
312//! }
313//! # }
314//! ```
315//!
316//! ## Examples
317//!
318//! The `examples/` directory contains comprehensive examples:
319//!
320//! - `basic_usage.rs` - Common Docker operations
321//! - `container_lifecycle.rs` - Container management patterns
322//! - `docker_compose.rs` - Docker Compose usage
323//! - `streaming.rs` - Real-time output streaming
324//! - `error_handling.rs` - Error handling patterns
325//!
326//! Run examples with:
327//!
328//! ```bash
329//! cargo run --example basic_usage
330//! cargo run --example streaming
331//! cargo run --features compose --example docker_compose
332//! ```
333//!
334//! ## Migration from Docker CLI
335//!
336//! Migrating from shell scripts to `docker-wrapper` is straightforward:
337//!
338//! **Shell:**
339//! ```bash
340//! docker run -d --name web -p 8080:80 nginx:latest
341//! ```
342//!
343//! **Rust:**
344//! ```rust,no_run
345//! # use docker_wrapper::{DockerCommand, RunCommand};
346//! # #[tokio::main]
347//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
348//! RunCommand::new("nginx:latest")
349//!     .detach()
350//!     .name("web")
351//!     .port(8080, 80)
352//!     .execute()
353//!     .await?;
354//! # Ok(())
355//! # }
356//! ```
357//!
358//! ## Contributing
359//!
360//! Contributions are welcome! Please see the [GitHub repository](https://github.com/joshrotenberg/docker-wrapper)
361//! for contribution guidelines and development setup.
362//!
363//! ## License
364//!
365//! This project is licensed under the MIT License - see the LICENSE file for details.
366
367#![warn(missing_docs)]
368#![warn(clippy::all)]
369#![warn(clippy::pedantic)]
370
371pub mod command;
372#[cfg(feature = "compose")]
373pub mod compose;
374pub mod debug;
375pub mod error;
376pub mod platform;
377pub mod prerequisites;
378pub mod stream;
379#[cfg(any(
380    feature = "templates",
381    feature = "template-redis",
382    feature = "template-redis-cluster",
383    feature = "template-postgres",
384    feature = "template-mysql",
385    feature = "template-mongodb",
386    feature = "template-nginx"
387))]
388pub mod template;
389
390pub use stream::{OutputLine, StreamHandler, StreamResult, StreamableCommand};
391
392pub use command::{
393    attach::{AttachCommand, AttachResult},
394    bake::BakeCommand,
395    build::{BuildCommand, BuildOutput},
396    builder::{BuilderBuildCommand, BuilderPruneCommand, BuilderPruneResult},
397    commit::{CommitCommand, CommitResult},
398    container_prune::{ContainerPruneCommand, ContainerPruneResult},
399    context::{
400        ContextCreateCommand, ContextInfo, ContextInspectCommand, ContextLsCommand,
401        ContextRmCommand, ContextUpdateCommand, ContextUseCommand,
402    },
403    cp::{CpCommand, CpResult},
404    create::{CreateCommand, CreateResult},
405    diff::{DiffCommand, DiffResult, FilesystemChange, FilesystemChangeType},
406    events::{DockerEvent, EventActor, EventsCommand, EventsResult},
407    exec::{ExecCommand, ExecOutput},
408    export::{ExportCommand, ExportResult},
409    history::{HistoryCommand, HistoryResult, ImageLayer},
410    image_prune::{DeletedImage, ImagePruneCommand, ImagePruneResult},
411    images::{ImageInfo, ImagesCommand, ImagesOutput},
412    import::{ImportCommand, ImportResult},
413    info::{DockerInfo as SystemDockerInfo, InfoCommand, InfoOutput, SystemInfo},
414    init::{InitCommand, InitOutput, InitTemplate},
415    inspect::{InspectCommand, InspectOutput},
416    kill::{KillCommand, KillResult},
417    load::{LoadCommand, LoadResult},
418    login::{LoginCommand, LoginOutput},
419    logout::{LogoutCommand, LogoutOutput},
420    logs::LogsCommand,
421    network::{
422        NetworkConnectCommand, NetworkConnectResult, NetworkCreateCommand, NetworkCreateResult,
423        NetworkDisconnectCommand, NetworkDisconnectResult, NetworkInfo, NetworkInspectCommand,
424        NetworkInspectOutput, NetworkLsCommand, NetworkLsOutput, NetworkPruneCommand,
425        NetworkPruneResult, NetworkRmCommand, NetworkRmResult,
426    },
427    pause::{PauseCommand, PauseResult},
428    port::{PortCommand, PortMapping as PortInfo, PortResult},
429    ps::{ContainerInfo, PsCommand, PsFormat, PsOutput},
430    pull::PullCommand,
431    push::PushCommand,
432    rename::{RenameCommand, RenameResult},
433    restart::{RestartCommand, RestartResult},
434    rm::{RmCommand, RmResult},
435    rmi::{RmiCommand, RmiResult},
436    run::{ContainerId, MountType, RunCommand, VolumeMount},
437    save::{SaveCommand, SaveResult},
438    search::{RepositoryInfo, SearchCommand, SearchOutput},
439    start::{StartCommand, StartResult},
440    stats::{ContainerStats, StatsCommand, StatsResult},
441    stop::{StopCommand, StopResult},
442    system::{
443        BuildCacheInfo, BuildCacheUsage, ContainerInfo as SystemContainerInfo, ContainerUsage,
444        DiskUsage, ImageInfo as SystemImageInfo, ImageUsage, PruneResult, SystemDfCommand,
445        SystemPruneCommand, VolumeInfo as SystemVolumeInfo, VolumeUsage,
446    },
447    tag::{TagCommand, TagResult},
448    top::{ContainerProcess, TopCommand, TopResult},
449    unpause::{UnpauseCommand, UnpauseResult},
450    update::{UpdateCommand, UpdateResult},
451    version::{ClientVersion, ServerVersion, VersionCommand, VersionInfo, VersionOutput},
452    volume::{
453        VolumeCreateCommand, VolumeCreateResult, VolumeInfo, VolumeInspectCommand,
454        VolumeInspectOutput, VolumeLsCommand, VolumeLsOutput, VolumePruneCommand,
455        VolumePruneResult, VolumeRmCommand, VolumeRmResult,
456    },
457    wait::{WaitCommand, WaitResult},
458    CommandExecutor, CommandOutput, DockerCommand, EnvironmentBuilder, PortBuilder, PortMapping,
459    Protocol,
460};
461pub use debug::{BackoffStrategy, DebugConfig, DebugExecutor, DryRunPreview, RetryPolicy};
462pub use error::{Error, Result};
463pub use platform::{Platform, PlatformInfo, Runtime};
464pub use prerequisites::{ensure_docker, DockerInfo, DockerPrerequisites};
465
466#[cfg(any(
467    feature = "templates",
468    feature = "template-redis",
469    feature = "template-redis-cluster",
470    feature = "template-postgres",
471    feature = "template-mysql",
472    feature = "template-mongodb",
473    feature = "template-nginx"
474))]
475pub use template::{Template, TemplateBuilder, TemplateConfig, TemplateError};
476
477// Redis templates
478#[cfg(feature = "template-redis")]
479pub use template::redis::{RedisInsightTemplate, RedisTemplate};
480
481#[cfg(feature = "template-redis")]
482pub use template::redis::{RedisSentinelTemplate, SentinelConnectionInfo, SentinelInfo};
483
484#[cfg(feature = "template-redis-cluster")]
485pub use template::redis::{
486    ClusterInfo, NodeInfo, NodeRole, RedisClusterConnection, RedisClusterTemplate,
487};
488
489#[cfg(feature = "template-redis-enterprise")]
490pub use template::redis::{RedisEnterpriseConnectionInfo, RedisEnterpriseTemplate};
491
492// Database templates
493#[cfg(feature = "template-postgres")]
494pub use template::database::{PostgresConnectionString, PostgresTemplate};
495
496#[cfg(feature = "template-mysql")]
497pub use template::database::{MysqlConnectionString, MysqlTemplate};
498
499#[cfg(feature = "template-mongodb")]
500pub use template::database::{MongodbConnectionString, MongodbTemplate};
501
502// Web server templates
503#[cfg(feature = "template-nginx")]
504pub use template::web::NginxTemplate;
505
506/// The version of this crate
507pub const VERSION: &str = env!("CARGO_PKG_VERSION");
508
509#[cfg(test)]
510mod tests {
511    use super::*;
512
513    #[test]
514    fn test_version() {
515        // Verify version follows semver format (major.minor.patch)
516        let parts: Vec<&str> = VERSION.split('.').collect();
517        assert!(parts.len() >= 3, "Version should have at least 3 parts");
518
519        // Verify each part is numeric
520        for part in &parts[0..3] {
521            assert!(
522                part.chars().all(|c| c.is_ascii_digit()),
523                "Version parts should be numeric"
524            );
525        }
526    }
527}