Example: Oneshot Retrieval
Relevant source files
The following files were used as context for generating this wiki page:
Oneshot retrieval refers to the process of initializing a battery manager, iterating through available power sources once, and extracting their current state without continuous monitoring or event-driven updates. This pattern is primarily used for CLI tools, system prompts (like the starship shell prompt), and diagnostic reports where a point-in-time snapshot of battery metrics is required.
The battery crate provides a unified, cross-platform API that abstracts away OS-specific interfaces (such as Linux sysfs, macOS IOKit, or NetBSD envsys) to return data in standardized SI measurement units.
Sources: README.md:21-25, src/lib.rs:1-10
Architecture and Components
The architecture for oneshot retrieval revolves around three primary abstractions: the Manager, the Batteries iterator, and the Battery device. The system uses a provider-based pattern where platform-specific implementations are hidden behind these common types.
Key Components
| Component | Description |
|---|---|
Manager | The entry point for the library. It discovers batteries connected to the system. |
Batteries | An iterator returned by the manager that yields individual battery devices. |
Battery | A high-level wrapper providing access to battery properties like state, technology, and charge. |
BatteryDevice | A trait that defines the interface for platform-specific battery implementations. |
Sources: src/lib.rs:27-28, src/platform/traits.rs:37-39
System Flow
The following diagram illustrates the flow from manager initialization to data retrieval:
The workflow begins with creating a manager, which identifies system-specific battery paths or handles. Iterating through the batteries triggers the underlying platform implementations to read and parse hardware data.
Sources: README.md:52-65, src/platform/traits.rs:25-34
Data Retrieval Logic
During oneshot retrieval, the library fetches several categories of data. These are typically recalculated from raw kernel values into SI units.
Instant Data Collection
Each platform implementation gathers "Instant Data" which represents the current snapshot. For example, on Linux, a DataBuilder collects values from sysfs, while on macOS, InstantData is populated from IOKit properties.
Derived Metrics
The library automatically calculates certain metrics if the underlying hardware does not provide them directly. This ensures a consistent API across different operating systems.
- State of Charge: Calculated as
energy / energy_full. - State of Health: Calculated as
energy_full / energy_full_design. - Time to Full/Empty: Calculated using
energy_rateand remaining/needed energy.
Sources: src/platform/traits.rs:42-53, src/platform/traits.rs:91-125
Platform Implementations
Different operating systems require distinct retrieval logic handled by the specific backends.
Linux (Sysfs)
The Linux backend reads from /sys/class/power_supply/. It uses a DataBuilder to lazily fetch and cache properties from files like energy_now, voltage_now, and status. It includes workarounds for hardware that reports current (µA) instead of power (µW).
Sources: src/platform/linux/sysfs/source.rs:98-120, src/platform/linux/sysfs/source.rs:163-180
macOS (IOKit)
The macOS backend interacts with the IOPMPowerSource service. It retrieves a CFDictionary of properties and converts keys like kIOPMPSCurrentCapacityKey and kIOPMPSVoltageKey into typed values. It handles specific cases for Apple Silicon (M-series) chips which report percentages in certain keys.
Sources: src/platform/darwin/iokit/power_source.rs:10-44, src/platform/darwin/iokit/power_source.rs:80-94
NetBSD (EnvSys)
The NetBSD implementation uses ioctl to communicate with the envsys framework. It specifically targets the acpibat driver and parses properties from a property list (plist), handling units like "Ampere hour" or "Watt hour".
Sources: src/platform/netbsd/device.rs:32-65, src/platform/netbsd/device.rs:104-122
Implementation Example
A typical oneshot retrieval implementation in Rust looks like this:
fn main() -> Result<(), battery::Error> {
// 1. Initialize the platform-specific manager
let manager = battery::Manager::new()?;
// 2. Iterate through discovered batteries
for (idx, maybe_battery) in manager.batteries()?.enumerate() {
let battery = maybe_battery?;
// 3. Retrieve typed information
println!("Battery #{}:", idx);
println!("State: {:?}", battery.state());
println!("Charge: {:?}", battery.state_of_charge());
println!("Voltage: {:?}", battery.voltage());
}
Ok(())
}
Sources: README.md:52-65, src/lib.rs:27-28
Summary
The oneshot retrieval pattern in the battery crate provides a robust mechanism for obtaining battery telemetry across diverse operating systems. By abstracting platform-specific details into a unified BatteryDevice trait and converting raw kernel data into SI units, it allows developers to write platform-agnostic code for monitoring battery health and status. The system is designed to be efficient for single-poll use cases while providing high accuracy through derived calculations when hardware data is missing.