Skip to main content

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}