# What is the Purpose of the openlogi-core Crate in OpenLogi?

> Discover the purpose of the openlogi-core crate. It offers a hardware-I/O-free foundation for Logitech HID++ devices, ensuring type consistency across all OpenLogi components.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: internals
- Published: 2026-08-21

---

**The `openlogi-core` crate provides the platform-agnostic data model and TOML-serializable configuration types for Logitech HID++ devices, serving as a pure, hardware-I/O-free foundation that enables unit testing without physical hardware and ensures type consistency across the CLI, agent, and GUI components.**

The `openlogi-core` crate acts as the foundational data layer for OpenLogi (AprilNEA/OpenLogi), a native Rust alternative to Logitech Options+. According to [`docs/DEVELOPMENT.md`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/DEVELOPMENT.md), this crate isolates the data definitions—device capabilities, configuration schemas, and action catalogs—from hardware communication and async runtimes, allowing the same `serde`-driven types to power every interface in the stack.

## Core Responsibilities of openlogi-core

### Pure Type Definitions and Serialization

At its heart, `openlogi-core` contains only Rust structs and enums that describe the entire domain model. These types implement `Serialize` and `Deserialize` from `serde`, enabling seamless conversion to and from TOML configuration files. As noted in [`crates/openlogi-core/README.md`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/README.md), the crate strictly avoids hardware-specific I/O, async runtimes, and operating system dependencies. This separation ensures that data structures can be unit-tested in isolation without requiring attached Logitech devices or platform-specific SDKs.

### Global Configuration Management

The crate defines the `Settings` struct in [`crates/openlogi-core/src/config/settings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config/settings.rs), which governs global defaults such as SmartShift behavior, DPI step increments, and LED color palettes. Each field carries serde attributes for default values:

- **`smart_shift`**: Boolean toggling automatic DPI shifting, defaulting to `true` via `default_smart_shift()`
- **`step`**: Global DPI step size, defaulting to `100` via `default_step()`
- **`colors`**: Vector of hex color strings for LED devices, defaulting to red, green, and blue via `default_colors()`
- **`assets_dir`**: Optional `PathBuf` to custom asset directories

Individual devices can override these global values in their specific configuration sections, as illustrated in [`docs/config.example.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/config.example.toml).

### Device Models and Action Catalogs

`openlogi-core` enumerates supported hardware and user interactions through strict type definitions. The crate maps:

- **Device identifiers**: Vendor and product IDs (e.g., `0x046d` for Logitech), DPI ranges, and button layouts defined in the device model source
- **Action kinds**: Enumerated variants for clicks, scrolls, key combinations, swipes, and DPI steps found in the action catalog
- **Bindings**: Structures linking physical buttons to actions, including per-application overrides and macro definitions

These definitions ensure that `openlogi-cli`, `openlogi-agent`, and `openlogi-desktop` all operate on identical data structures without duplication.

## The I/O-Free Architecture Design

### Testability Without Hardware Dependencies

By isolating data models from the `async-hid` and raw input handling found in sibling crates like `openlogi-hid` and `openlogi-hook`, `openlogi-core` enables comprehensive unit testing. Developers can instantiate `Settings`, `Binding`, and `Device` structs in test suites without mocking USB HID protocols or attaching physical mice. This design is documented in the crate's [`README.md`](https://github.com/AprilNEA/OpenLogi/blob/main/README.md) as a deliberate strategy to validate configuration logic and action parsing independently of hardware state.

### Cross-Crate Consistency

The crate functions as the single source of truth for the entire workspace. Because `openlogi-core` contains no platform-specific code, it compiles identically on macOS, Linux, and Windows. The agent binary adds the runtime HID++ I/O layer, the CLI adds command-line parsing, and the GUI adds GPUI presentation, but all three consume the same `Settings` and `Action` definitions from [`src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/src/lib.rs).

## Working with openlogi-core: Code Examples

### Loading TOML Configuration Files

To load a configuration file into the `Settings` struct, use the synchronous helper `load_from_path` defined in [`crates/openlogi-core/src/config/file.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config/file.rs):

```rust
use openlogi_core::config::file::load_from_path;

fn main() -> anyhow::Result<()> {
    let settings = load_from_path("docs/config.example.toml")?;
    println!("SmartShift enabled? {}", settings.smart_shift);
    println!("Global DPI step: {}", settings.step);
    Ok(())
}

```

This function reads the file via `std::fs::read_to_string` and parses the TOML content using `toml::from_str`, returning a fully populated `Settings` instance or a descriptive error via `anyhow`.

### Creating Configuration Programmatically

You can construct `Settings` instances directly and serialize them to TOML for generating default configurations:

```rust
use openlogi_core::config::Settings;
use toml;

fn main() -> anyhow::Result<()> {
    let custom = Settings {
        smart_shift: false,
        step: 200,
        colors: vec!["#ff0000".into(), "#00ff00".into()],
        assets_dir: None,
    };
    
    let toml_str = toml::to_string_pretty(&custom)?;
    println!("{}", toml_str);
    Ok(())
}

```

This approach leverages the `Default` trait implementation and the default functions `default_smart_shift()`, `default_step()`, and `default_colors()` defined in [`src/config/settings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/src/config/settings.rs).

### Defining Button Bindings

The crate provides types for describing input bindings, allowing you to define what actions trigger when specific buttons are pressed:

```rust
use openlogi_core::binding::{Binding, Action, ActionKind};

fn create_thumb_binding() -> Binding {
    Binding {
        device: "G502".into(),
        button: "thumb_button".into(),
        action: Action {
            kind: ActionKind::KeyCombo,
            keys: vec!["Ctrl".into(), "Alt".into(), "Delete".into()],
            ..Default::default()
        },
    }
}

```

These binding definitions correspond directly to the `[[binding]]` tables in [`docs/config.example.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/config.example.toml), where buttons map to actions like `key_combo` or `dpi_step`.

## Summary

- **Pure Data Layer**: `openlogi-core` contains `serde`-serializable structs for `Settings`, `Device`, and `Action` types without hardware I/O or async code.
- **Configuration Management**: Defines global defaults and device-specific overrides via the `Settings` struct in [`src/config/settings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/src/config/settings.rs), loadable via `load_from_path` in [`src/config/file.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/src/config/file.rs).
- **Cross-Component Foundation**: Shared by `openlogi-cli`, `openlogi-agent`, and `openlogi-desktop` to ensure consistent data interpretation across the stack.
- **Testing Isolation**: Enables unit testing of configuration logic and binding rules without physical Logitech devices attached.

## Frequently Asked Questions

### Does openlogi-core communicate directly with Logitech devices?

No. According to [`docs/DEVELOPMENT.md`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/DEVELOPMENT.md) and the crate's [`README.md`](https://github.com/AprilNEA/OpenLogi/blob/main/README.md), `openlogi-core` contains no hardware I/O. Direct HID++ communication is handled by separate crates such as `openlogi-hid` and `openlogi-agent`, while `openlogi-core` remains limited to data definitions and synchronous file loading for configuration.

### How does openlogi-core handle TOML parsing?

The crate utilizes `serde` derive macros on structs like `Settings` combined with the `toml` crate for serialization. The `load_from_path` function in [`src/config/file.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/src/config/file.rs) orchestrates file reading and parsing, while default values for fields like `smart_shift` and `step` are provided via dedicated default functions referenced in serde attributes.

### Which OpenLogi binaries depend on openlogi-core?

Three primary binaries consume this crate: `openlogi-cli` for diagnostics and configuration editing, `openlogi-agent` for the runtime device management layer, and `openlogi-desktop` (along with `openlogi-overlay`) for the graphical interface. This shared dependency ensures that configuration changes in TOML files are interpreted identically across all interfaces.

### Why is the openlogi-core crate considered platform-agnostic?

The crate contains no platform-specific code, async runtimes, or hardware drivers—only pure Rust data structures. While it performs synchronous file I/O for configuration loading, it avoids OS-specific APIs found in crates like `openlogi-hook` (macOS CGEventTap, Linux evdev) or `openlogi-inject`, allowing it to compile and function identically on macOS, Linux, and Windows.