Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6d3f7bf
feat: add hm-util crate skeleton with os module structure
markovejnovic May 23, 2026
d626198
feat(hm-util): add os::dirs for platform directory resolution
markovejnovic May 23, 2026
fe3f48d
feat(hm-util): implement os::fs with async + blocking atomic file I/O
markovejnovic May 23, 2026
4a6c316
refactor: migrate fs_util callers to hm-util::os::fs::blocking
markovejnovic May 23, 2026
7274073
refactor: delegate directory resolution to hm-util::os::dirs
markovejnovic May 23, 2026
c32ae6b
refactor: replace custom CancellationToken with tokio_util::sync::Can…
markovejnovic May 23, 2026
63a14a3
docs: update CLAUDE.md files for hm-util extraction
markovejnovic May 23, 2026
9567b33
Merge remote-tracking branch 'origin/main' into refactor/code-reorg
markovejnovic May 23, 2026
428f684
fix: use direct anyhow dep in hm-util after main merge
markovejnovic May 23, 2026
db57676
better dirs
markovejnovic May 23, 2026
189da39
refactor(hm-util): remove anyhow dep, implement dirs modules
markovejnovic May 23, 2026
7c52724
refactor: consolidate dirs into hm_util::dirs, remove os::dirs
markovejnovic May 23, 2026
2dedf63
refactor: domain-specific dir accessors, hide raw platform calls
markovejnovic May 23, 2026
ccbf7c8
refactor: suppress redundant_pub_crate at workspace level
markovejnovic May 23, 2026
6cc77e9
deslop
markovejnovic May 23, 2026
20f30f4
feat(hm-util): add windows crate for atomic file replacement
markovejnovic May 23, 2026
775d1d5
feat(hm-util): add atomic_rename_over with ReplaceFileW on Windows
markovejnovic May 23, 2026
716a455
refactor(hm-util): use atomic_rename_over_sync in write_atomic_restri…
markovejnovic May 23, 2026
a45d21e
refactor(hm-util): unify blocking wrappers via block_in_place
markovejnovic May 23, 2026
3d8af12
refactor(hm-util): async-first fs module, eliminate sync indirection
markovejnovic May 23, 2026
61aaf74
refactor(hm-util): use tokio::fs::rename instead of std::fs::rename
markovejnovic May 23, 2026
2923fbb
refactor(hm-util): convert fs helpers to async tokio operations
markovejnovic May 23, 2026
73c10e0
manual cleanup
markovejnovic May 23, 2026
b01d397
deslop
markovejnovic May 23, 2026
b16bc83
more cleanup
markovejnovic May 23, 2026
f1d51a8
more cleanup
markovejnovic May 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
The `cli/` directory is a Cargo workspace.

- `crates/hm/` — the `hm` binary (today's CLI body).
- `crates/hm-util/` — shared OS and filesystem utilities.
- `crates/hm-plugin-protocol/` — wire types (serde structs only).
- `crates/hm-plugin-sdk/` — authoring SDK for plugin writers.
- `crates/hm-fixtures/` — test-only WASM plugins; compiled to
Expand Down
63 changes: 62 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ members = [
"crates/hm-plugin-output-json",
"crates/hm-plugin-cloud",
"crates/hm-fixtures",
"crates/hm-util",
]
default-members = [
"crates/hm",
"crates/hm-plugin-protocol",
"crates/hm-plugin-sdk",
"crates/hm-util",
]

[workspace.package]
Expand All @@ -24,6 +26,7 @@ repository = "https://github.com/harmont-dev/harmont-cli"
[workspace.dependencies]
hm-plugin-protocol = { path = "crates/hm-plugin-protocol", version = "0.0.0-dev" }
hm-plugin-sdk = { path = "crates/hm-plugin-sdk", version = "0.0.0-dev" }
hm-util = { path = "crates/hm-util", version = "0.0.0-dev" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
schemars = { version = "0.8", features = ["preserve_order", "semver", "uuid1", "chrono"] }
Expand Down Expand Up @@ -54,6 +57,9 @@ cargo_common_metadata = "allow"
# schemars 0.8 pulls older indexmap / wit-bindgen transitively; we
# can't fix without bumping schemars.
multiple_crate_versions = "allow"
# `pub(crate)` inside a non-pub module triggers this, but `unreachable_pub`
# already enforces the same invariant from the rustc side.
redundant_pub_crate = "allow"
dbg_macro = "deny"
todo = "deny"
unimplemented = "deny"
Expand Down
25 changes: 25 additions & 0 deletions crates/hm-util/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "hm-util"
version = "0.0.0-dev"
edition.workspace = true
license.workspace = true
repository.workspace = true
description = "Shared OS and filesystem utilities for Harmont crates."

[dependencies]
dirs = "6"
tokio = { version = "1", features = ["rt", "fs"] }

[target.'cfg(windows)'.dependencies.windows]
version = "0.62"
features = [
"Win32_Foundation",
"Win32_Storage_FileSystem",
]

[dev-dependencies]
tempfile = "3"
tokio = { version = "1", features = ["full", "test-util"] }

[lints]
workspace = true
62 changes: 62 additions & 0 deletions crates/hm-util/src/dirs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//! Harmont-specific directory resolution.
//!
//! Every directory accessor in this module returns a Harmont-namespaced
//! path. Raw platform primitives (`home_dir`, `config_dir`) live in
//! `os::dirs` and are **not** re-exported — callers outside `hm-util`
//! should never need them.

#![allow(clippy::must_use_candidate)]

use std::path::PathBuf;

use crate::os::dirs as platform;

/// `~/.harmont/` — CLI config home (config.toml, credentials.toml).
pub fn harmont_config_dir() -> Option<PathBuf> {
platform::home_dir().map(|h| h.join(".harmont"))
}

/// `<config_dir>/harmont/` — XDG-aware data root (plugins, state).
pub fn harmont_data_dir() -> Option<PathBuf> {
platform::config_dir().map(|c| c.join("harmont"))
}

/// `<config_dir>/harmont/plugins/` — user-global plugin directory.
pub fn harmont_plugins_dir() -> Option<PathBuf> {
harmont_data_dir().map(|d| d.join("plugins"))
}

/// `<config_dir>/harmont/state/` — per-plugin persistent KV state.
pub fn harmont_plugin_state_dir() -> Option<PathBuf> {
harmont_data_dir().map(|d| d.join("state"))
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;

#[test]
fn harmont_config_dir_under_home() {
let p = harmont_config_dir().unwrap();
assert!(p.ends_with(".harmont"));
}

#[test]
fn harmont_data_dir_under_config() {
let p = harmont_data_dir().unwrap();
assert!(p.ends_with("harmont"));
}

#[test]
fn harmont_plugins_dir_resolves() {
let p = harmont_plugins_dir().unwrap();
assert!(p.ends_with("harmont/plugins"));
}

#[test]
fn harmont_plugin_state_dir_resolves() {
let p = harmont_plugin_state_dir().unwrap();
assert!(p.ends_with("harmont/state"));
}
}
2 changes: 2 additions & 0 deletions crates/hm-util/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod os;
pub mod dirs;
14 changes: 14 additions & 0 deletions crates/hm-util/src/os/dirs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//! Raw platform directory primitives.
//!
//! This module is `pub(crate)` — external callers must use
//! [`crate::dirs`] which provides Harmont-specific accessors.

use std::path::PathBuf;

pub(crate) fn home_dir() -> Option<PathBuf> {
dirs::home_dir()
}

pub(crate) fn config_dir() -> Option<PathBuf> {
dirs::config_dir()
}
Loading
Loading