cargo_image_runner/lib.rs
1//! cargo-image-runner: A generic, highly customizable embedded/kernel development runner for Rust.
2//!
3//! This library provides a flexible framework for building and running bootable images with
4//! support for multiple bootloaders (Limine, GRUB, none), image formats (ISO, FAT, directory),
5//! and boot types (BIOS, UEFI, hybrid).
6//!
7//! # Quick Start
8//!
9//! ## Standalone Usage (no `cargo_metadata` or `clap` required)
10//!
11//! ```no_run
12//! use cargo_image_runner::{builder, Config};
13//!
14//! # fn main() -> cargo_image_runner::Result<()> {
15//! let config = Config::from_toml_str(r#"
16//! [boot]
17//! type = "uefi"
18//! [bootloader]
19//! kind = "none"
20//! [image]
21//! format = "directory"
22//! "#)?;
23//!
24//! builder()
25//! .with_config(config)
26//! .workspace_root(".")
27//! .executable("target/x86_64-unknown-none/debug/my-kernel")
28//! .run()
29//! # }
30//! ```
31//!
32//! ## Using Cargo.toml Metadata (requires `cargo-metadata` feature)
33//!
34//! ```no_run
35//! # #[cfg(feature = "cargo-metadata")]
36//! # fn main() -> cargo_image_runner::Result<()> {
37//! use cargo_image_runner::builder;
38//!
39//! // Load configuration from Cargo.toml and run
40//! builder()
41//! .from_cargo_metadata()?
42//! .no_bootloader()
43//! .directory_output()
44//! .qemu()
45//! .run()
46//! # }
47//! # #[cfg(not(feature = "cargo-metadata"))]
48//! # fn main() {}
49//! ```
50//!
51//! ## Configuration in Cargo.toml
52//!
53//! ```toml
54//! [package.metadata.image-runner.boot]
55//! type = "uefi"
56//!
57//! [package.metadata.image-runner.bootloader]
58//! kind = "none"
59//!
60//! [package.metadata.image-runner.image]
61//! format = "directory"
62//! ```
63//!
64//! ## With Limine Bootloader
65//!
66//! ```toml
67//! [package.metadata.image-runner.boot]
68//! type = "hybrid" # Supports both BIOS and UEFI
69//!
70//! [package.metadata.image-runner.bootloader]
71//! kind = "limine"
72//! config-file = "limine.conf"
73//!
74//! [package.metadata.image-runner.bootloader.limine]
75//! version = "v8.4.0-binary"
76//!
77//! [package.metadata.image-runner.variables]
78//! TIMEOUT = "5"
79//! ```
80//!
81//! Then create `limine.conf`:
82//!
83//! ```text
84//! timeout: {{TIMEOUT}}
85//!
86//! /My Kernel
87//! protocol: limine
88//! kernel_path: boot():/boot/{{EXECUTABLE_NAME}}
89//! ```
90//!
91//! # I/O Capture & Streaming
92//!
93//! The [`IoHandler`] trait enables programmatic interaction with QEMU's serial port:
94//! - **Capture** serial output for test assertions ([`CaptureHandler`])
95//! - **Tee** output to both capture and terminal ([`runner::io::TeeHandler`])
96//! - **React** to patterns and send input ([`runner::io::PatternResponder`])
97//!
98//! ```no_run
99//! use cargo_image_runner::{builder, Config, CaptureHandler};
100//!
101//! # fn main() -> cargo_image_runner::Result<()> {
102//! let config = Config::from_toml_str(r#"
103//! [boot]
104//! type = "uefi"
105//! [bootloader]
106//! kind = "none"
107//! [image]
108//! format = "directory"
109//! "#)?;
110//!
111//! let result = builder()
112//! .with_config(config)
113//! .workspace_root(".")
114//! .executable("target/x86_64-unknown-none/debug/my-kernel")
115//! .io_handler(CaptureHandler::new())
116//! .run_with_result()?;
117//!
118//! if let Some(output) = &result.captured_output {
119//! if let Some(serial) = &output.serial {
120//! println!("Serial output: {serial}");
121//! }
122//! }
123//! # Ok(())
124//! # }
125//! ```
126//!
127//! # Architecture
128//!
129//! The library is built around three core traits:
130//!
131//! - [`Bootloader`](bootloader::Bootloader): Prepares bootloader files and configuration
132//! - [`ImageBuilder`](image::ImageBuilder): Builds bootable images in various formats
133//! - [`Runner`](runner::Runner): Executes images (e.g., in QEMU)
134//! - [`IoHandler`](runner::io::IoHandler): Handles I/O from running instances
135//!
136//! These traits allow easy extensibility for custom bootloaders, image formats, and runners.
137//!
138//! # Custom Bootloader Example
139//!
140//! ```no_run
141//! use cargo_image_runner::bootloader::{Bootloader, BootloaderFiles, ConfigFile};
142//! use cargo_image_runner::config::BootType;
143//! use cargo_image_runner::core::{Context, Result};
144//!
145//! struct MyBootloader;
146//!
147//! impl Bootloader for MyBootloader {
148//! fn prepare(&self, ctx: &Context) -> Result<BootloaderFiles> {
149//! Ok(BootloaderFiles::new())
150//! }
151//!
152//! fn config_files(&self, ctx: &Context) -> Result<Vec<ConfigFile>> {
153//! Ok(Vec::new())
154//! }
155//!
156//! fn boot_type(&self) -> BootType {
157//! BootType::Uefi
158//! }
159//!
160//! fn name(&self) -> &str {
161//! "MyBootloader"
162//! }
163//! }
164//! ```
165//!
166//! # Features
167//!
168//! - `default` - Enables `cli`, `cargo-metadata`, `uefi`, `bios`, `limine`, `iso`, and `qemu`
169//! - `cli` - CLI binary and argument parsing (implies `cargo-metadata`)
170//! - `cargo-metadata` - Load config from `Cargo.toml` via `cargo_metadata`
171//! - `uefi` - UEFI boot support (includes OVMF firmware)
172//! - `bios` - BIOS boot support
173//! - `limine` - Limine bootloader (requires git)
174//! - `grub` - GRUB bootloader
175//! - `iso` - ISO image format
176//! - `fat` - FAT filesystem image format
177//! - `qemu` - QEMU runner
178//! - `progress` - Progress reporting (optional)
179//!
180//! For standalone library use without `clap` or `cargo_metadata`:
181//!
182//! ```toml
183//! [dependencies]
184//! cargo-image-runner = { version = "0.5", default-features = false, features = ["uefi", "qemu"] }
185//! ```
186
187pub mod bootloader;
188pub mod config;
189pub mod core;
190pub mod firmware;
191pub mod image;
192pub mod runner;
193pub mod util;
194
195// Re-export commonly used types
196pub use crate::core::{Error, ImageRunner, ImageRunnerBuilder, Result};
197pub use config::{BootType, BootloaderKind, Config, ImageFormat, SerialConfig, SerialMode};
198pub use runner::io::{CaptureHandler, CapturedIo, IoAction, IoHandler};
199pub use runner::{CapturedOutput, RunResult};
200
201/// Create a new image runner builder.
202///
203/// This is the main entry point for the fluent API.
204///
205/// # Example
206///
207/// ```no_run
208/// use cargo_image_runner::{builder, Config};
209///
210/// # fn main() -> cargo_image_runner::Result<()> {
211/// let config = Config::from_toml_str(r#"
212/// [boot]
213/// type = "uefi"
214/// [bootloader]
215/// kind = "none"
216/// [image]
217/// format = "directory"
218/// "#)?;
219///
220/// builder()
221/// .with_config(config)
222/// .workspace_root(".")
223/// .executable("target/x86_64-unknown-none/debug/my-kernel")
224/// .run()
225/// # }
226/// ```
227pub fn builder() -> ImageRunnerBuilder {
228 ImageRunnerBuilder::new()
229}