docs: 添加项目文档AGENTS.md
添加详细的zoxide项目文档,包含技术栈、项目结构、构建测试命令、代码风格指南、测试说明、安全考虑、架构细节和发布部署信息
This commit is contained in:
parent
67ca1bc959
commit
d52c6336b8
|
|
@ -0,0 +1,244 @@
|
|||
# zoxide
|
||||
|
||||
zoxide is a smarter `cd` command for your terminal, inspired by `z` and `autojump`.
|
||||
It remembers which directories you use most frequently, so you can "jump" to them
|
||||
in just a few keystrokes. It works on all major shells.
|
||||
|
||||
This is a Rust CLI project. The repository lives at
|
||||
<https://github.com/ajeetdsouza/zoxide>.
|
||||
|
||||
---
|
||||
|
||||
## Technology stack
|
||||
|
||||
- **Language**: Rust (edition 2024, MSRV 1.85.0)
|
||||
- **CLI parsing**: `clap` v4 with derive macros
|
||||
- **Template engine**: `askama` (for generating shell integration scripts)
|
||||
- **Database serialization**: `bincode`
|
||||
- **Error handling**: `anyhow`
|
||||
- **Self-referencing structs**: `ouroboros` (used by the database layer)
|
||||
- **Task runner**: `just`
|
||||
- **Dev environment**: Nix (`shell.nix`)
|
||||
|
||||
---
|
||||
|
||||
## Project structure
|
||||
|
||||
```
|
||||
.
|
||||
├── Cargo.toml # Package manifest
|
||||
├── build.rs # Generates shell completions into contrib/completions/
|
||||
├── justfile # Task definitions (lint, test, fmt)
|
||||
├── rustfmt.toml # Rustfmt configuration
|
||||
├── shell.nix # Reproducible Nix development shell
|
||||
├── Cross.toml # Cross-compilation configuration
|
||||
│
|
||||
├── src/
|
||||
│ ├── main.rs # Entry point: parses args, runs commands, handles SilentExit
|
||||
│ ├── config.rs # Reads environment variables (_ZO_DATA_DIR, _ZO_ECHO, etc.)
|
||||
│ ├── error.rs # SilentExit and BrokenPipeHandler traits
|
||||
│ ├── util.rs # Fzf wrapper, atomic file writes, path resolution, time utils
|
||||
│ ├── shell.rs # Askama template structs for each supported shell
|
||||
│ │
|
||||
│ ├── cmd/ # CLI subcommand implementations
|
||||
│ │ ├── mod.rs # Run trait and command dispatch
|
||||
│ │ ├── cmd.rs # Clap derive structs for all subcommands and enums
|
||||
│ │ ├── add.rs # `zoxide add` — add/increment directory rank
|
||||
│ │ ├── query.rs # `zoxide query` — search database (list/interactive/first)
|
||||
│ │ ├── init.rs # `zoxide init` — render shell integration script
|
||||
│ │ ├── import.rs # `zoxide import` — import from autojump / z
|
||||
│ │ ├── remove.rs # `zoxide remove` — remove directory from database
|
||||
│ │ └── edit.rs # `zoxide edit` — interactive database editor via fzf
|
||||
│ │
|
||||
│ └── db/ # Database layer
|
||||
│ ├── mod.rs # Database struct (open, save, add, remove, age, dedup, sort)
|
||||
│ ├── dir.rs # Dir struct (path, rank, last_accessed, score, display)
|
||||
│ └── stream.rs # Stream iterator for querying directories with filters
|
||||
│
|
||||
├── templates/ # Askama templates for shell integration scripts
|
||||
│ ├── bash.txt, zsh.txt, fish.txt, powershell.txt, ...
|
||||
│
|
||||
├── contrib/completions/ # Generated completion files (updated by build.rs)
|
||||
│
|
||||
├── man/man1/ # Man pages
|
||||
│
|
||||
└── tests/
|
||||
└── completions.rs # Integration tests for generated completion scripts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build and test commands
|
||||
|
||||
### Standard (without Nix)
|
||||
|
||||
```bash
|
||||
# Build debug binary
|
||||
cargo build
|
||||
|
||||
# Build release binary
|
||||
cargo build --release
|
||||
|
||||
# Run tests
|
||||
cargo test
|
||||
|
||||
# Run lints
|
||||
cargo clippy --all-features --all-targets -- -Dwarnings
|
||||
|
||||
# Format code
|
||||
cargo fmt --all
|
||||
```
|
||||
|
||||
### With Nix (recommended on Unix)
|
||||
|
||||
The project uses a pure Nix shell for CI and local development. All linting and
|
||||
testing tools are pinned there.
|
||||
|
||||
```bash
|
||||
# Enter dev shell
|
||||
nix-shell
|
||||
|
||||
# Run lints (includes rustfmt, clippy, msrv, udeps, shellcheck, markdownlint, ...)
|
||||
just lint
|
||||
|
||||
# Run tests (uses cargo-nextest when inside nix-shell)
|
||||
just test
|
||||
|
||||
# Format everything (Rust, Nix, shell, YAML, Markdown)
|
||||
just fmt
|
||||
```
|
||||
|
||||
### Important notes
|
||||
|
||||
- `build.rs` generates shell completions into `contrib/completions/`. It is not
|
||||
a no-op — modifying CLI definitions in `src/cmd/cmd.rs` will regenerate these
|
||||
files on the next build.
|
||||
- The `nix-dev` Cargo feature gates tests that require external binaries
|
||||
(shells, fzf, shellcheck, shfmt, black, mypy, pylint, fish_indent, etc.).
|
||||
These tests only run when the feature is enabled and the tools are available.
|
||||
|
||||
---
|
||||
|
||||
## Code style guidelines
|
||||
|
||||
- Use `cargo fmt` with the project's `rustfmt.toml`.
|
||||
- Key settings:
|
||||
- `group_imports = "StdExternalCrate"`
|
||||
- `imports_granularity = "Module"`
|
||||
- `style_edition = "2024"`
|
||||
- `use_try_shorthand = true`
|
||||
- `use_field_init_shorthand = true`
|
||||
- Prefer `?` and `anyhow::Result` for error propagation.
|
||||
- Use `SilentExit` for intentional early exits that should not print an error
|
||||
message.
|
||||
- Use `BrokenPipeHandler::pipe_exit` when writing to stdout/stderr to handle
|
||||
broken pipes gracefully (e.g. `| head`).
|
||||
- Keep platform-specific code behind `cfg(unix)` / `cfg(windows)`.
|
||||
- Only use `unsafe` when absolutely necessary (the project currently uses it
|
||||
only once, to forcibly disable Rust backtrace environment variables at startup).
|
||||
|
||||
---
|
||||
|
||||
## Testing instructions
|
||||
|
||||
### Unit tests
|
||||
|
||||
Embedded in source files under `#[cfg(test)]`:
|
||||
|
||||
- `src/db/mod.rs` — database add/remove persistence
|
||||
- `src/db/stream.rs` — keyword matching algorithm (parameterized with `rstest`)
|
||||
- `src/cmd/import.rs` — autojump and z importer logic
|
||||
|
||||
Run them with:
|
||||
|
||||
```bash
|
||||
cargo test
|
||||
```
|
||||
|
||||
### Integration tests
|
||||
|
||||
- `tests/completions.rs` — loads generated completion scripts into bash, fish,
|
||||
zsh, and PowerShell to verify they parse without errors. Requires the
|
||||
`nix-dev` feature.
|
||||
|
||||
### Shell integration tests
|
||||
|
||||
Located in `src/shell.rs` under `#[cfg(feature = "nix-dev")]`.
|
||||
These are the most comprehensive tests: they render every Askama template for
|
||||
all combinations of options and then:
|
||||
|
||||
- Execute the generated script in the actual shell binary (bash, dash, zsh,
|
||||
fish, elvish, nushell, pwsh, tcsh, xonsh)
|
||||
- Run linters on the generated script (shellcheck, shfmt, fish_indent, black,
|
||||
mypy, pylint)
|
||||
|
||||
Because they require many external tools, they are gated behind the `nix-dev`
|
||||
feature and are intended to run inside the Nix shell.
|
||||
|
||||
```bash
|
||||
# Run all tests including nix-dev tests
|
||||
nix-shell --pure --run "cargo nextest run --all-features --no-fail-fast --workspace"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security considerations
|
||||
|
||||
- **Atomic database writes**: The database is written atomically using a
|
||||
temporary file + `fs::rename` to prevent corruption on crashes. On Unix, the
|
||||
temporary file preserves the original file's owner via `fchown`.
|
||||
- **Windows executable resolution**: On Windows, `fzf.exe` is resolved via the
|
||||
`which` crate before spawning to avoid the current-directory executable search
|
||||
behavior of `CreateProcess`.
|
||||
- **Path validation**: `add` rejects paths containing newlines or carriage
|
||||
returns. Symlink resolution is optional and controlled by `_ZO_RESOLVE_SYMLINKS`.
|
||||
- **Database size limit**: Deserialization enforces a 32 MiB maximum to prevent
|
||||
malformed input from causing excessive memory use.
|
||||
- **Backtrace suppression**: The binary forcibly unsets `RUST_BACKTRACE` and
|
||||
`RUST_LIB_BACKTRACE` at startup to avoid leaking internal paths in error
|
||||
output.
|
||||
|
||||
---
|
||||
|
||||
## Key architectural details
|
||||
|
||||
### Database
|
||||
|
||||
- Stored as a binary file (`db.zo`) in the data directory, serialized with
|
||||
`bincode`. The format has a version header (current version: 3).
|
||||
- Each entry has a `path`, `rank` (f64), and `last_accessed` (Unix epoch).
|
||||
- The `Database` struct is self-referencing (via `ouroboros`) because `Dir`
|
||||
contains a `Cow<'a, str>` borrowed from the deserialized bytes.
|
||||
- Scoring applies time-based multipliers: entries accessed within the last hour
|
||||
get 4x, within a day 2x, within a week 0.5x, older 0.25x.
|
||||
- Aging keeps total rank bounded. When the sum exceeds `_ZO_MAXAGE` (default
|
||||
10000), all ranks are scaled down and entries below rank 1.0 are removed.
|
||||
- Non-existent directories are lazily removed during queries if they have not
|
||||
been accessed within a 3-month TTL.
|
||||
|
||||
### Shell integration
|
||||
|
||||
- `zoxide init <shell>` renders an Askama template from `templates/` to stdout.
|
||||
- The generated script defines `z` (jump), `zi` (interactive jump), and a hook
|
||||
that calls `zoxide add` automatically.
|
||||
- Hook modes: `none` (never), `prompt` (every prompt), `pwd` (on directory
|
||||
change, default).
|
||||
|
||||
### Query flow
|
||||
|
||||
1. Open the database.
|
||||
2. Create a `Stream` with filters (keywords, base_dir, exclude globs, exists check).
|
||||
3. Stream sorts entries by score and returns them one by one.
|
||||
4. For interactive mode, results are piped into `fzf`.
|
||||
5. Save the database (lazy cleanup may mark it dirty).
|
||||
|
||||
---
|
||||
|
||||
## Release and deployment
|
||||
|
||||
- CI runs on `ubuntu-latest` inside a Nix shell for lints and tests.
|
||||
- The release workflow cross-compiles for many targets (Linux musl, Android,
|
||||
macOS, Windows) using `cross`, packages them as `.tar.gz`, `.zip`, and `.deb`,
|
||||
and uploads artifacts.
|
||||
- A draft GitHub release is automatically created when a commit on `main`
|
||||
starts with `chore(release)`.
|
||||
Loading…
Reference in New Issue