diff --git a/.gitignore b/.gitignore index 7afea8e..5e6a36d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,11 @@ result .direnv/ **/.claude/settings.local.json + +AGENTS.override.md + +AGENTS.md + +.cargo/ +.cclsp.json +.mcp.json diff --git a/AGENTS.md b/AGENTS.md index 8f4372f..848d3ef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,3 +1,5 @@ + + # Repository Guidelines ## Project Structure & Module Organization diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8f4372f --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,26 @@ +# Repository Guidelines + +## Project Structure & Module Organization +The runtime crate lives under `src/`, where `main.rs` wires the CLI in `cli.rs` to the filtering core in `filter/`. Shared utilities (`config.rs`, `telemetry.rs`, `logger.rs`) keep side-effects isolated. Integration and property tests sit in `tests/`, with reusable fixtures in `test-helpers/`. Performance work belongs in `benches/`, while `fuzz/` houses `cargo fuzz` targets. Reference material and generated manuals land in `docs/`, and the `xtask/` helper orchestrates doc builds. + +## Build, Test, and Development Commands +- `cargo build --release` compiles the filter binary with production optimizations. +- `cargo test --all` runs unit, integration, and property suites; mirror CI before opening a PR. +- `cargo clippy --all-targets -- -D warnings` enforces lint cleanliness; match CI expectations. +- `cargo fmt --all` or `./dev.sh fmt` keeps formatting consistent; run before committing. +- `./dev.sh all` chains fmt, clippy, and tests for a quick gate. +- `cargo run --package xtask --bin xtask -- generate-docs` refreshes the CLI/manpage material in `docs/`. +- `nix develop` drops you into the flake-provisioned dev shell; use `./dev.sh nix` for a full build. + +## Coding Style & Naming Conventions +Rust code follows standard 4-space indentation and `rustfmt` defaults. Modules, files, and test helpers use `snake_case`; types remain `PascalCase`. Prefer explicit `use crate::...` paths for clarity, and document non-obvious helpers with `///` comments. CI treats clippy warnings as errors, so resolve lints locally. + +## Runtime Configuration Tips +- `--ignore-key ` lets you exempt specific controls (e.g., `KEY_VOLUMEDOWN` encoder wheels) from debouncing; the flag accepts symbolic names or numeric codes. +- Keep `--log-bounces` on when developing input pipelines so freshly ignored keys can be verified quickly via the systemd journal. + +## Testing Guidelines +Keep fast checks in `tests/` and push slower fuzz targets under `fuzz/`. Name new integration files `*_tests.rs`; within modules, group cases under `mod tests { ... }`. Run `cargo test --all` before pushing, and refresh relevant property tests when touching timing logic. For fuzzing, seed with `cargo fuzz run fuzz_core_filter` or `fuzz_target_stats` and commit any minimized corpus updates. Benchmarks live in `benches/filter.rs`; use `cargo bench` to validate performance-sensitive changes. + +## Commit & Pull Request Guidelines +History favors concise, imperative subjects (e.g., `Tighten near-miss logging`); include a brief body when context is not obvious. Squash fixups locally so each commit passes `./dev.sh all`. PRs should link issues when applicable, summarize behavioural impact, and cite how you validated the change. Attach log snippets or screenshots for user-visible output shifts, and call out configuration updates in `docs/` or sample pipelines. diff --git a/Cargo.lock b/Cargo.lock index fb98846..1128899 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -273,9 +273,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cast" @@ -1087,9 +1087,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-traits" @@ -1808,9 +1808,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.44" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", @@ -1818,22 +1818,22 @@ dependencies = [ "num-conv", "num_threads", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.24" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", diff --git a/flake.lock b/flake.lock index 1f9269a..bb6671f 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1741473158, - "narHash": "sha256-kWNaq6wQUbUMlPgw8Y+9/9wP0F8SHkjy24/mN3UAppg=", + "lastModified": 1768818222, + "narHash": "sha256-460jc0+CZfyaO8+w8JNtlClB2n4ui1RbHfPTLkpwhU8=", "owner": "numtide", "repo": "devshell", - "rev": "7c9e793ebe66bcba8292989a68c0419b737a22a0", + "rev": "255a2b1725a20d060f566e4755dbf571bbbb5f76", "type": "github" }, "original": { @@ -38,11 +38,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1722073938, - "narHash": "sha256-OpX0StkL8vpXyWOGUD6G+MA26wAXK6SpT94kLJXo6B4=", + "lastModified": 1762156382, + "narHash": "sha256-Yg7Ag7ov5+36jEFC1DaZh/12SEXo6OO3/8rqADRxiqs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e36e9f57337d0ff0cf77aceb58af4c805472bfae", + "rev": "7241bcbb4f099a66aafca120d37c65e8dda32717", "type": "github" }, "original": { @@ -54,11 +54,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1760038930, - "narHash": "sha256-Oncbh0UmHjSlxO7ErQDM3KM0A5/Znfofj2BSzlHLeVw=", + "lastModified": 1777954456, + "narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0b4defa2584313f3b781240b29d61f6f9f7e0df3", + "rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1", "type": "github" }, "original": { @@ -97,11 +97,11 @@ "nixpkgs": "nixpkgs_3" }, "locked": { - "lastModified": 1760323082, - "narHash": "sha256-SKhC9tyt+gVgQHnZGMVPSdptlDYNqApT56JF5t8RwBY=", + "lastModified": 1777950921, + "narHash": "sha256-NpOgt8ISaHTDNJZjNUfwFfbieKfRXzab4WKM31gZCGA=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "c73e6874fe8dce0bab82c0387b510875f1eff9f8", + "rev": "366ea19e0e55b768f74b7a0b2a20f847e7ae828d", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 0bf0e5b..29bf336 100644 --- a/flake.nix +++ b/flake.nix @@ -186,8 +186,8 @@ }) // { nixosModules = { - default = import ./nix/modules/intercept-bounce.nix { inherit self; }; - intercept-bounce = import ./nix/modules/intercept-bounce.nix { inherit self; }; + default = import ./nix/modules/intercept-bounce.nix {inherit self;}; + intercept-bounce = import ./nix/modules/intercept-bounce.nix {inherit self;}; }; }; } diff --git a/nix/modules/intercept-bounce.nix b/nix/modules/intercept-bounce.nix index 23f3b06..70ae106 100644 --- a/nix/modules/intercept-bounce.nix +++ b/nix/modules/intercept-bounce.nix @@ -1,7 +1,11 @@ -{ self }: -{ config, lib, pkgs, ... }: -let - inherit (lib) +{self}: { + config, + lib, + pkgs, + ... +}: let + inherit + (lib) concatMap escapeShellArgs mkEnableOption @@ -11,7 +15,7 @@ let types ; - packagesForSystem = lib.attrByPath [ pkgs.system ] (self.packages or { }) { }; + packagesForSystem = lib.attrByPath [pkgs.system] (self.packages or {}) {}; defaultPackage = packagesForSystem.intercept-bounce @@ -21,37 +25,39 @@ let cfg = config.services.interceptBounce; toStr = value: - if builtins.isString value then value else builtins.toString value; + if builtins.isString value + then value + else builtins.toString value; baseArgs = [] - ++ optionals (cfg.debounceTime != null) [ "--debounce-time" cfg.debounceTime ] + ++ optionals (cfg.debounceTime != null) ["--debounce-time" cfg.debounceTime] ++ optionals (cfg.nearMissThresholdTime != null) [ "--near-miss-threshold-time" cfg.nearMissThresholdTime ] - ++ optionals (cfg.logInterval != null) [ "--log-interval" cfg.logInterval ] - ++ optionals cfg.logAllEvents [ "--log-all-events" ] - ++ optionals cfg.logBounces [ "--log-bounces" ] - ++ optionals cfg.listDevices [ "--list-devices" ] - ++ optionals cfg.statsJson [ "--stats-json" ] - ++ optionals cfg.verbose [ "--verbose" ] + ++ optionals (cfg.logInterval != null) ["--log-interval" cfg.logInterval] + ++ optionals cfg.logAllEvents ["--log-all-events"] + ++ optionals cfg.logBounces ["--log-bounces"] + ++ optionals cfg.listDevices ["--list-devices"] + ++ optionals cfg.statsJson ["--stats-json"] + ++ optionals cfg.verbose ["--verbose"] ++ optionals (cfg.ringBufferSize != null) [ "--ring-buffer-size" - toStr cfg.ringBufferSize + toStr + cfg.ringBufferSize ] - ++ concatMap (key: [ "--debounce-key" key ]) cfg.debounceKeys - ++ concatMap (key: [ "--ignore-key" key ]) cfg.ignoreKeys + ++ concatMap (key: ["--debounce-key" key]) cfg.debounceKeys + ++ concatMap (key: ["--ignore-key" key]) cfg.ignoreKeys ++ optionals (cfg.otelEndpoint != null) [ "--otel-endpoint" cfg.otelEndpoint ] ++ cfg.extraArgs; - commandList = [ "${cfg.package}/bin/intercept-bounce" ] ++ baseArgs; + commandList = ["${cfg.package}/bin/intercept-bounce"] ++ baseArgs; commandString = escapeShellArgs commandList; -in -{ +in { options.services.interceptBounce = { enable = mkEnableOption "intercept-bounce CLI integration"; @@ -126,13 +132,13 @@ in debounceKeys = mkOption { type = types.listOf types.str; - default = [ ]; + default = []; description = "List of keys passed via repeated --debounce-key flags."; }; ignoreKeys = mkOption { type = types.listOf types.str; - default = [ ]; + default = []; description = "List of keys passed via repeated --ignore-key flags."; }; @@ -144,13 +150,13 @@ in extraArgs = mkOption { type = types.listOf types.str; - default = [ ]; + default = []; description = "Arbitrary arguments appended after generated flags."; }; command = mkOption { type = types.listOf types.str; - default = [ ]; + default = []; description = "Resolved intercept-bounce invocation expressed as a list."; }; @@ -162,7 +168,7 @@ in }; config = mkIf cfg.enable { - environment.systemPackages = optionals cfg.installSystemPackage [ cfg.package ]; + environment.systemPackages = optionals cfg.installSystemPackage [cfg.package]; services.interceptBounce.command = commandList; services.interceptBounce.commandString = commandString; }; diff --git a/src/filter/stats.rs b/src/filter/stats.rs index 4a5b1fc..1d4b0ea 100644 --- a/src/filter/stats.rs +++ b/src/filter/stats.rs @@ -151,11 +151,7 @@ impl TimingHistogram { /// Calculates the average timing in microseconds. Returns 0 if count is 0. pub fn average_us(&self) -> u64 { - if self.count > 0 { - self.sum_us / self.count - } else { - 0 - } + self.sum_us.checked_div(self.count).unwrap_or(0) } // Add methods like get_buckets(), get_count() if needed externally.