Compact instruction file covering workspace layout, the non-obvious default-members behavior (bare cargo run builds core, not tui), the 12 CoreError variants, the CRITICAL TerminalGuard Drop caveat, per-module temp-file test helpers, and the benchmark test-file generation requirement.
6.6 KiB
logViewer
High-performance log file viewer. Rust workspace with 4 crates, edition 2024, MSRV 1.92 (pinned in rust-toolchain.toml).
Workspace Layout
crates/core — log-viewer-core (library) — I/O, parsing, types, config, file watching
crates/tui — log-viewer-tui (binary) — terminal UI (ratatui + crossterm)
crates/gui — log-viewer-gui (binary) — GUI (egui + eframe) — STUB, not yet functional
crates/bench — log-viewer-bench (binary) — mmap vs pread benchmark harness
core owns all I/O, parsing, data types, and file watching. TUI/GUI consume core's public API. GUI is a placeholder with no core integration yet.
default-members = ["crates/core"] — bare cargo run / cargo test target core, not the TUI. Always pass -p log-viewer-tui (or --workspace) explicitly.
Commands
# Run the TUI (primary interface)
cargo run -p log-viewer-tui -- <log-file>
# Build/check/test/lint the whole workspace
cargo check --workspace
cargo test --workspace
cargo fmt --check --all
cargo clippy --workspace -- -D warnings
# Single crate
cargo test -p log-viewer-core
cargo test -p log-viewer-tui
# Benchmarks (test file is NOT auto-generated — see "Benchmarks" section below)
cargo run -p log-viewer-bench
cargo run -p log-viewer-bench -- --quick --suites startup,render --output results.md
CI Gate (must pass before merge)
Runs on ubuntu-latest + windows-latest (.github/workflows/ci.yml):
cargo fmt --check --allcargo check --workspacecargo test --workspacecargo clippy --workspace -- -D warnings
Toolchain installed via dtolnay/rust-toolchain@stable reading rust-toolchain.toml (channel 1.92). No rustfmt.toml or clippy.toml — uses toolchain defaults.
Core Architecture
All ten modules are declared pub in lib.rs and re-exported as log_viewer_core::<module>.
Key Modules
types—LogLevel,LogEntry,SearchQuery,SearchFilter,Bookmark,FileSession,DuplicateKey,SearchResulterror—CoreError(12 variants via thiserror: Io, Parse, Search, Index, Config, TomlSerialize, Encoding, Watch, Mmap, Cache, FileNotFound, Other) +Result<T>alias.Fromimpls forio::Error/serde_json::Error/toml::de::Error/toml::ser::Error/notify::Errormean?works freely.io::file_reader— mmap-backedFileReaderwith TOCTOU mitigation (post-mmap stat check), append/reload detectionio::progressive_reader—ProgressiveFileReaderstate machine (ReaderState::Sampling { .. } | Ready { .. }), background indexer via crossbeam-channel,VisualHeightIndexfor wrapped-line scrollio::line_index— Sparse index (everyBLOCK_SIZE = 256lines, memchr SIMD). Serializable to disk.io::index_cache— Persistent cache with xxh3 content hash, atomic writes via temp filesio::line_sampler/io::cache_util— shared helpers used by the index pathio::wrap— Unicode-aware line wrapping (CJK/emoji/tab), JSON pretty-printing; enforcesMAX_WRAP_INPUT_LENio::read_cache— LRU read cache (not yet integrated into the live reader; future pread backend)parser::json— NDJSON parser with BOM handling, duplicate key detectionparser::level— JSON-first level detection (detect_level); falls back to bounded keyword scan with word-boundary check on non-JSON lineswatcher::file_watcher— File event watcher (notify + crossbeam) with append/truncate/rotation detectionconfig—ColorConfigTOML load/save with per-field serde defaults
Stubs (single-line TODOs — planned, not yet built)
filter::Filterbookmark::BookmarkManagersession::SessionManagersearch::engine::SearchEngine
TUI Architecture
main.rs— clap CLI (files: Vec<String>),TerminalGuardRAII for raw mode + alternate screen. CRITICAL:Dropdoes not run onstd::process::exitorpanic = "abort"— never callprocess::exitinside the guarded scope; returnErrand use?instead. Event loop: poll indexer → poll watcher → draw → poll keys, 100ms timeout.app.rs—Appstruct (~4000 lines including ~135 inline tests, test mod starts at#[cfg(test)] mod tests).AppLoadingState: Empty | Loading { reader, estimated_lines, progress_percent } | Ready { reader } | Error(String).AppMode: Normal | Settings. Viewport cache for scroll. All key handling.ui.rs— ratatui rendering: title bar, content area, status bar, settings popupcolor.rs—LogLevel→ ratatuiColorviaColorConfig
TUI Keybindings
j/k scroll, Ctrl+d/u half-page, Ctrl+f/b full-page, G/gg jump end/top, Tab toggle JSON formatting, S settings panel, q/Esc quit. Settings: j/k select level, ←/→ cycle colors, Enter save, Esc cancel.
Testing
All tests are inline (#[cfg(test)] mod tests blocks). No tests/ directories, no integration tests, no async tests.
- ~450 tests total: core 251, tui 136, bench 63, gui 0
- Temp-file helpers vary per module — check the local
testsmod before assuming an API:make_temp_file(content) -> PathBuf—tui/app.rs,tui/ui.rsmake_test_file(lines) -> NamedTempFile—core/io/index_cache.rsmake_file(data) -> NamedTempFile—core/io/read_cache.rsstruct TempFile { ... }—core/io/line_sampler.rs
instais a declared dev-dependency but currently unused (noinsta::calls anywhere in the workspace)- TUI render tests use ratatui
TestBackend(seetui/ui.rs) - Bench crate has its own unit tests for mmap/pread backends, metrics, and data_gen helpers
Benchmarks
Custom harness (not criterion). Wall-clock + /proc/self/ RSS/page fault metrics. 7 suites: startup, render, jump, memory, growth, rotation, concurrent. Tests mmap (plain/sequential/random/populate/phase_aware) vs pread (plain/random/sequential) backends. Output: markdown tables in benchmark-report.md.
The harness does NOT auto-generate the test file. main.rs requires /tmp/test-logviewer/extreme.log to already exist; if missing it prints the dd command and exits. Generate it manually first:
mkdir -p /tmp/test-logviewer
dd if=/dev/urandom of=/tmp/test-logviewer/extreme.log bs=1M count=5000 # ~5GB
(crates/bench/src/data_gen.rs has generate_test_file / ensure_test_file helpers and is used by the growth and rotation suites for per-run growable files, but it is not wired into main.rs for the primary test file.)
Note: log-viewer-bench depends directly on memmap2 / nix / libc / memchr — it does not depend on log-viewer-core, so it can be built standalone with cargo build -p log-viewer-bench.