onyx
Cross-platform Rust toolkit for building desktop applications without rewriting OS-specific glue every time.
Sister crate to github.com/akira-io/onyx (Go). Mirrors the same module surface so a team can split a desktop app between a Rust core and Go services and reach for the same primitives on both sides.
Without onyx
use env;
use PathBuf;
use Command;
With onyx
use ;
let app = for_app;
let config = app.config?;
reveal_in_file_manager?;
let claude = new
.lookup
.lookup
.resolve?;
Same behavior on macOS, Linux, and Windows. No cfg!(target_os = ...) switches, no hand-rolled XDG logic, no per-OS shell invocations leaked into application code.
Modules
| Module | Purpose |
|---|---|
osinfo |
Platform detection (Platform::current, is_darwin, is_linux, is_windows, executable_extension). |
paths |
Per-app config, data, cache, logs directories with XDG / Library / AppData resolution. |
files |
Open paths/URLs with the default application, reveal a path in the file manager. |
shell |
Resolve CLI binaries via PATH first, then well-known per-platform install directories. |
Status
v0.1.0. Feature parity with onyx (Go) at v1.0.2. API stable within a minor version pre-1.0.
Design notes
shell::Resolver: one verb, two cases
The Resolver started with two separate concepts: a name list for PATH lookups and a fallback path list for explicit files to try when PATH missed. Resolution exposed a ResolutionSource enum (Path, Fallback, Unknown) so callers could see how the binary was found.
The split asked callers to classify each input upfront. The classification is mechanical: if the string has a path separator (/, \\) or a Windows drive prefix (C:), it is a path; otherwise it is a name. The source tag was rarely inspected.
Resolver now collapses everything to a single ordered list of targets. lookup accepts both. resolve returns the absolute PathBuf of the first target that resolves, or ShellError::BinaryNotFound. Callers that genuinely need to know how a binary was located inspect the returned path themselves.
Publishing
This crate publishes to crates.io via Trusted Publishing. The publish.yml workflow exchanges a GitHub OIDC token for a short-lived crates.io token at publish time. No long-lived API tokens are stored in repo secrets.
Bootstrap (one-time, for the very first release)
Trusted Publishing requires the crate to exist on crates.io before it can be configured. To bootstrap:
- Generate a one-time API token at https://crates.io/me with
publish-newscope. - Run locally:
This claims theonyx-rscrate name and ships v0.1.0. - On https://crates.io/crates/onyx-rs/settings, open Trusted Publishing and click Add:
- Publisher: GitHub
- Repository:
akira-io/onyx-rs - Workflow:
publish.yml - Environment:
release
- Revoke the one-time API token. Future releases trigger the
publish.ymlworkflow on everyvX.Y.Ztag push and no token is needed. - In this repo's GitHub Settings, create an Environment named
release(optionally with required reviewers as a pre-publish gate).
Cutting a release
- Bump
Cargo.tomlversion. - Promote the
## [Unreleased]block inCHANGELOG.mdto a dated## [X.Y.Z]block. - Commit with
chore(release): vX.Y.Z. - Tag and push:
publish.ymlverifies the tag matchesCargo.toml, runs tests, authenticates with crates.io via OIDC, publishes, and creates a GitHub Release.
Installation
[]
= "0.1"
Crate publishes as onyx-rs on crates.io (the onyx name was already taken). The library is still imported as onyx:
use ;
Platforms
macOS, Linux, Windows. Builds and tests run on all three in CI.
License
MIT.