mirror of
https://github.com/devmobasa/wayscriber.git
synced 2026-11-01 07:36:42 +01:00
Borrowing a descriptor requires it to be open at that moment, so the broker entry cannot check a raw number through a borrow. Check both numbers with getsockopt first, as main did, which keeps the rejection of closed descriptors and makes the borrow's safety claim true. The entry exits 126 either way, so this only restores the contract. Pin that rejection with an integration test against the real binary.
8.7 KiB
8.7 KiB
AGENTS.md
Scope
- This file applies to the whole wayscriber workspace.
- wayscriber is a Rust desktop annotation app for Wayland compositors.
- The workspace root crate is
wayscriber;configurator/is a separate GTK4/libadwaita (Relm4) desktop configurator crate. - Both crates use Rust 1.99.1 and edition 2024;
rust-toolchain.tomlpins the development toolchain. - The workspace default member is only the root crate; use
-p wayscriber-configuratoror--workspacewhen configurator coverage is needed.
Architecture
src/main.rsis a thin wrapper around the publicwayscriber::run_from_env()entry facade.src/lib.rsowns the canonical application module graph: reusable modules remain public for tests and the configurator, while runtime modules stay private behind the entry facade.- Core domains are stable shared values, backend/Wayland runtime, input state, drawing data/rendering, overlay UI, capture, config, session persistence, daemon lifecycle, canvas export, and configurator UI.
- Start with
README.md,CONTRIBUTING.md,docs/codebase-overview.md,docs/CONFIG.md, andconfigurator/README.mdwhen changing behavior.
Invariants
- Preserve foreground and fullscreen safety: do not launch, foreground, focus, or interact with another app/window, especially fullscreen apps, without explicit user permission.
- Treat
cargo run,wayscriber --active,wayscriber --daemon,wayscriber-configurator, and manual UI checks as potentially foreground/focus-affecting unless proven otherwise. - Preserve feature-gate intent for
tablet-input,dbus,portal,tray, andconfig-schema. - Do not edit generated/build output such as
target/. - Preserve existing Rust module style. This repo intentionally mixes
foo.rs,foo/, andfoo/mod.rs. - For Rust split modules, an
AGENTS.mdinfoo/does not apply to siblingfoo.rs; put shared rules in the parent guide.
Code Readability
- Let
rustfmtown mechanical Rust layout. Use standard Rust spacing:call(value),name: Type,T: Trait, andleft + right; omit spaces inside parentheses and around::,.,?, and ranges, and omit unnecessary control-flow parentheses. - Separate logical steps within functions with one blank line: validation and early exits, setup, execution or mutation, state and effect publication, and returning or mapping the result. Keep closely related statements together; add spacing when the purpose changes rather than after every statement.
- Keep a
let ... else,match, or parsed value with its immediate validation and error path. Use?for ordinary error propagation instead of expanding it solely to imitate another language's guard style. - Let
rustfmtdecide wrapping, same-line braces,} else {, indentation, and multiline collection or argument layout. Do not use formatter-skip attributes to force a competing layout. - Apply this grouping consistently to production Rust and tests. Preserve intentional blank lines while editing and formatting.
- Review changed functions after
cargo fmt. Formatter success verifies mechanical layout; also verify visually that logical chunks are clearly separated and easy to scan. - Apply these rules to new and materially changed code. Do not reformat unrelated existing code solely to adopt them.
Rust Style
- Rust code follows the Google Rust style guide as application code. Its rules that Clippy can check are workspace lints in
Cargo.toml, configured inclippy.toml. Fix a warning rather than suppressing it. When a site truly cannot comply, use#[expect(lint, reason = "...")]with the concrete constraint, never#[allow]. - Each
usestatement imports from one module:use foo::bar::{Bar, Baz};, notuse foo::{Foo, bar::Bar};.tests/repository_guards/use_statements.rschecks this because rustfmt'simports_granularityis unstable. - Do not use glob imports, except
use super::*in test modules and third-party preludes. - Import type and trait names and use them unqualified; call functions through their module (
fs::read). Keep ambiguous names such asio::Errorandfmt::Resultmodule-qualified. A gated import carries the same#[cfg(...)]as the code that uses it. - Borrow instead of cloning or taking ownership (
&str,&[T],Option<&T>). Replace more than three bool parameters with enums or an options struct. - Use
expectwith a reason instead ofunwrapin production code. Give assertions messages that explain the invariant, and document panics in# Panics. - Never reach inside a poisoned lock. Use
expectwhen the critical section cannot panic, or a fallback that leaves the data alone. Tests that serialize on process-wide state usetest_env::lock()or atest_env::Lease. catch_unwindis reserved for the reviewed worker-isolation sites that carry an#[expect]reason. Tests observe panics through a joined scoped thread.- Getters are
foo()/foo_mut(), a fallible constructor istry_new, and type lifetimes have descriptive names. Everyunsafeblock has a// SAFETY:comment that says why it is sound. - Make kernel calls through
stdorrustix, which need nounsafe, and rename on import when a module name clashes withstd(use rustix::fs as rustix_fs). Use rawlibccalls only where neither fits: signal handlers, the post-clonebroker stub,fail_stop, the zero-signal pidfd check, validating the broker's inherited descriptors, the inotify header parser,dup2onto a fixed descriptor number, andlocaltime_r.libcconstants stay fine. - Convert numbers losslessly with
From(f64::from(width)) wherever it can be called;constinitializers keepas. Take the middle of two values withmidpoint(f64::midpoint(a, b)), which cannot overflow. - Inline named format arguments:
format!("{name}: {err}"). - Use
map_or,map_or_else,is_some_and, oris_ok_andinstead ofmap(..).unwrap_or(..). Uselet ... elsefor amatchthat only unwraps or leaves, andif let ... elsefor amatchwith one pattern and a fallback. - In doc comments, put identifiers and protocol names in backticks (
wl_output). Product names that docs write plainly go indoc-valid-identsinclippy.toml. - Take raw pointers with
&raw const/&raw mut, change pointer constness withcast_const/cast_mut, and write the anonymous lifetime in paths (Formatter<'_>). Give every#[ignore]a reason:#[ignore = "needs a running Wayland compositor"]. - Clippy pedantic lints are enabled one at a time in
Cargo.toml, never as the whole group, so a toolchain update does not turn on new ones.
Coupled Changes
- Config changes often require
config.example.toml,docs/CONFIG.md, configurator models/views, schema behavior, and tests. - Tool/action/keybinding changes often require config defaults, action metadata, help/command UI, toolbar UI, configurator labels/search, docs, and tests.
- Daemon, service, shortcut, path, and packaging changes often need updates across
src/daemon/,src/paths/,src/systemd_user_service.rs,src/shortcut_hint.rs,configurator/src/app/daemon_setup/, andpackaging/. - Dependency changes must satisfy
deny.toml. A new license or duplicate version needs an entry there with its reason.
Validation
- For a fast full-suite run while iterating, use
cargo nextest run --workspace --all-features. Each test gets its own process, so it runs in parallel despite the native-font race that keeps the gate serial. It skips doctests and the isolated render tests, so it does not replace the gate. - Full local CI is
./tools/lint-and-test.sh. It needs the .NET SDK selected byglobal.json. Without it,cargo teststill runs the Rust source guards intests/repository_guards(process sites, config writers, shared dependencies, use statements, no Python), but not the C# checks, including the source-coverage check that finds.rsfiles Cargo never compiles. - That script runs the C# repository checks (assets, version, nixpkgs recipe, source coverage, legacy tools), the C# tool tests (which also run the packaging shell contracts),
cargo fmt --all -- --check, clippy with all targets/features, all-feature tests, and no-default-feature tests. - Run
git diff --checkbefore handoff. When relevant files are untracked and the index must stay unchanged, check those files directly as well. - For docs-only
AGENTS.mdedits, make new files visible to Git before whitespace checks, for examplerg --files --hidden -g AGENTS.md -0 | xargs -0 git add -N --followed bygit diff --check. - On PowerShell, use
rg --files --hidden -g AGENTS.md | ForEach-Object { git add -N -- $_ }followed bygit diff --check. - If you do not want to alter the index, run an explicit trailing-whitespace check across the untracked
AGENTS.mdfiles instead of relying on plaingit diff --check.