What Is the openlogi-core Crate in OpenLogi? The Foundation Data Layer

The openlogi-core crate serves as the platform-agnostic foundation of the AprilNEA/OpenLogi project, containing pure data structures and logic for device configurations, bindings, and actions while strictly excluding I/O and OS-specific code to ensure portability across WebAssembly and native targets.

The openlogi-core crate establishes the shared vocabulary that powers the entire OpenLogi ecosystem. As implemented in the AprilNEA/OpenLogi repository, this Rust crate defines the data models for Logitech device descriptions, action catalogs, and on-disk configuration without coupling to any specific runtime environment. Every other component—including the desktop GUI, background agent, and HID++ communication layers—depends on this crate for type-safe data interchange.

Core Responsibilities of the openlogi-core Crate

Pure Data and Logic Definitions

At its heart, openlogi-core contains only pure data and logic that can be shared across all architectural layers. The crate defines the fundamental structures that describe Logitech hardware capabilities, user-configurable actions, and persistence schemas. According to the crate-level documentation in src/lib.rs, it provides "Shared types and configuration for OpenLogi," ensuring that concepts like DPI settings, SmartShift parameters, and lighting profiles have a single source of truth.

Platform Agnostic by Design

The crate deliberately excludes any platform-specific code such as hidpp, async-hid, or OS-specific event APIs. These implementation details live in sibling crates like openlogi-hid, openlogi-agent, and openlogi-desktop. This strict separation means openlogi-core never touches the host environment directly, making it suitable for targets where system access is restricted or unavailable. The project enforces this boundary through a "portable-crates" CI job that verifies the crate compiles correctly for WebAssembly.

Optional Feature Gating

While the base crate contains only pure data, optional Cargo features extend its capabilities when the target platform permits:

  • fs: Enables file system operations for reading and writing the TOML configuration file via the paths module
  • locale: Adds locale negotiation logic for selecting UI language preferences

Both features are gated behind explicit flags, allowing the core crate to remain lean when compiled for constrained environments like WebAssembly.

Key Source Files and Architecture

The crate's implementation is organized into focused modules that collectively form the model layer:

  • src/lib.rs — Crate-level documentation and public module re-exports
  • src/config.rs — Defines the Config struct and device configuration schema
  • src/binding/*.rs — Contains types for button bindings, gestures, and action mappings
  • src/device.rs — Represents Logitech HID++ device capabilities and metadata
  • src/action_ring.rs — Implements the ActionRing data structure for the circular Actions Ring UI component
  • src/locale.rs (feature-gated) — Handles user locale negotiation
  • src/paths.rs (feature-gated) — Provides platform-agnostic configuration file paths

These files define the configuration schema and binding types that the rest of the application operates on, ensuring that openlogi-desktop/src/services/config.rs and openlogi-agent/src/main.rs share identical data definitions.

Cross-Crate Integration and Benefits

Because openlogi-core maintains strict separation from I/O concerns, downstream crates can import the same type definitions without pulling in heavy OS bindings. This architecture delivers three critical advantages:

  1. Portable — Compiles for any Rust target, including WebAssembly, enabling future web-based configuration interfaces
  2. Stable — Changes to the shared datamodel are confined to a single location in src/config.rs and related files, preventing synchronization errors between the GUI and agent
  3. Reusable — Both the GUI (openlogi-desktop) and background agent (openlogi-agent) depend on it for configuration handling, DPI settings, and action definitions

Practical Usage Examples

Loading Configuration Files

When the fs feature is enabled, applications can load user preferences from disk:

use openlogi_core::config::Config;

// Reads ~/.config/openlogi/config.toml
let cfg = Config::load().expect("Failed to read OpenLogi config");
println!("Configured DPI: {}", cfg.device.dpi);

Defining Button Bindings

Create type-safe binding definitions that work across GUI and agent contexts:

use openlogi_core::binding::{Binding, Action};

let binding = Binding {
    button: openlogi_core::binding::button::Button::Left,
    modifiers: None,
    action: Action::KeyCombo(vec!['C', 'C', 'C']),
};

Iterating Over Device Configurations

Process multiple device definitions using standard iterator patterns:

for dev in cfg.devices.iter() {
    println!("Device {} – DPI range {}–{}", dev.name, dev.dpi.min, dev.dpi.max);
}

Managing the Action Ring

Manipulate the circular action selector used in the desktop interface:

use openlogi_core::action_ring::ActionRing;

let ring = ActionRing::default();
ring.select_next(); // Rotate the ring

Summary

  • The openlogi-core crate functions as the model layer of OpenLogi, containing pure data structures for devices, configurations, and actions
  • It maintains strict platform independence by excluding I/O code and OS-specific APIs, enabling WebAssembly compilation
  • Optional fs and locale features provide filesystem and internationalization capabilities when needed
  • Sibling crates including openlogi-desktop, openlogi-agent, and openlogi-hid consume these types for consistent data interchange
  • Key files like src/config.rs and src/binding/*.rs serve as the single source of truth for the project's data schema

Frequently Asked Questions

What is the primary role of the openlogi-core crate in OpenLogi?

The crate serves as the foundational data model layer that defines shared types for device descriptions, button bindings, and configuration schemas. It enables the GUI, agent, and HID layers to communicate using identical data structures without duplicating type definitions across the codebase.

How does openlogi-core maintain platform independence?

The crate deliberately excludes hardware communication libraries like hidpp or async-hid, containing only pure Rust data structures and logic. Platform-specific code resides in sibling crates, while openlogi-core remains compatible with WebAssembly and other constrained targets through its feature-gated design.

Which OpenLogi components depend on openlogi-core?

Both the desktop GUI (openlogi-desktop) and the background service (openlogi-agent) import this crate to handle configuration parsing and device modeling. The HID++ communication layer also references these types when translating raw hardware events into the application's internal action representation.

What optional features does openlogi-core provide?

The crate offers two primary optional features: fs, which adds filesystem operations for reading ~/.config/openlogi/config.toml, and locale, which enables user language preference detection. Both are disabled by default to preserve portability when compiling for targets without standard filesystem access.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →