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
380pub use stream::{OutputLine, StreamHandler, StreamResult, StreamableCommand};
381
382pub use command::{
383 attach::{AttachCommand, AttachResult},
384 bake::BakeCommand,
385 build::{BuildCommand, BuildOutput},
386 builder::{BuilderBuildCommand, BuilderPruneCommand, BuilderPruneResult},
387 commit::{CommitCommand, CommitResult},
388 container_prune::{ContainerPruneCommand, ContainerPruneResult},
389 cp::{CpCommand, CpResult},
390 create::{CreateCommand, CreateResult},
391 diff::{DiffCommand, DiffResult, FilesystemChange, FilesystemChangeType},
392 events::{DockerEvent, EventActor, EventsCommand, EventsResult},
393 exec::{ExecCommand, ExecOutput},
394 export::{ExportCommand, ExportResult},
395 history::{HistoryCommand, HistoryResult, ImageLayer},
396 image_prune::{DeletedImage, ImagePruneCommand, ImagePruneResult},
397 images::{ImageInfo, ImagesCommand, ImagesOutput},
398 import::{ImportCommand, ImportResult},
399 info::{DockerInfo as SystemDockerInfo, InfoCommand, InfoOutput, SystemInfo},
400 inspect::{InspectCommand, InspectOutput},
401 kill::{KillCommand, KillResult},
402 load::{LoadCommand, LoadResult},
403 login::{LoginCommand, LoginOutput},
404 logout::{LogoutCommand, LogoutOutput},
405 logs::LogsCommand,
406 network::{
407 NetworkConnectCommand, NetworkConnectResult, NetworkCreateCommand, NetworkCreateResult,
408 NetworkDisconnectCommand, NetworkDisconnectResult, NetworkInfo, NetworkInspectCommand,
409 NetworkInspectOutput, NetworkLsCommand, NetworkLsOutput, NetworkPruneCommand,
410 NetworkPruneResult, NetworkRmCommand, NetworkRmResult,
411 },
412 pause::{PauseCommand, PauseResult},
413 port::{PortCommand, PortMapping as PortInfo, PortResult},
414 ps::{ContainerInfo, PsCommand, PsFormat, PsOutput},
415 pull::PullCommand,
416 push::PushCommand,
417 rename::{RenameCommand, RenameResult},
418 restart::{RestartCommand, RestartResult},
419 rm::{RmCommand, RmResult},
420 rmi::{RmiCommand, RmiResult},
421 run::{ContainerId, MountType, RunCommand, VolumeMount},
422 save::{SaveCommand, SaveResult},
423 search::{RepositoryInfo, SearchCommand, SearchOutput},
424 start::{StartCommand, StartResult},
425 stats::{ContainerStats, StatsCommand, StatsResult},
426 stop::{StopCommand, StopResult},
427 system::{
428 BuildCacheInfo, BuildCacheUsage, ContainerInfo as SystemContainerInfo, ContainerUsage,
429 DiskUsage, ImageInfo as SystemImageInfo, ImageUsage, PruneResult, SystemDfCommand,
430 SystemPruneCommand, VolumeInfo as SystemVolumeInfo, VolumeUsage,
431 },
432 tag::{TagCommand, TagResult},
433 top::{ContainerProcess, TopCommand, TopResult},
434 unpause::{UnpauseCommand, UnpauseResult},
435 update::{UpdateCommand, UpdateResult},
436 version::{ClientVersion, ServerVersion, VersionCommand, VersionInfo, VersionOutput},
437 volume::{
438 VolumeCreateCommand, VolumeCreateResult, VolumeInfo, VolumeInspectCommand,
439 VolumeInspectOutput, VolumeLsCommand, VolumeLsOutput, VolumePruneCommand,
440 VolumePruneResult, VolumeRmCommand, VolumeRmResult,
441 },
442 wait::{WaitCommand, WaitResult},
443 CommandExecutor, CommandOutput, DockerCommand, EnvironmentBuilder, PortBuilder, PortMapping,
444 Protocol,
445};
446pub use debug::{BackoffStrategy, DebugConfig, DebugExecutor, DryRunPreview, RetryPolicy};
447pub use error::{Error, Result};
448pub use platform::{Platform, PlatformInfo, Runtime};
449pub use prerequisites::{ensure_docker, DockerInfo, DockerPrerequisites};
450
451/// The version of this crate
452pub const VERSION: &str = env!("CARGO_PKG_VERSION");
453
454#[cfg(test)]
455mod tests {
456 use super::*;
457
458 #[test]
459 fn test_version() {
460 // Verify version follows semver format (major.minor.patch)
461 let parts: Vec<&str> = VERSION.split('.').collect();
462 assert!(parts.len() >= 3, "Version should have at least 3 parts");
463
464 // Verify each part is numeric
465 for part in &parts[0..3] {
466 assert!(
467 part.chars().all(|c| c.is_ascii_digit()),
468 "Version parts should be numeric"
469 );
470 }
471 }
472}