refactor: implement modular architecture - #1
Merged
Conversation
- Extract CLI definitions (Cli, Commands, STYLES, help text) into cli.rs - Extract console utilities (ANSI, standalone detection, pause) into console.rs - Move check_admin() from main.rs to privilege.rs - Consolidate all display functions into display.rs (banner, clean output) - Add SmartCleanResult struct to cleaner.rs for data-driven output - Reduce main.rs from 702 to ~270 lines (thin entry point only) - Update documentation to reflect new module structure
Using std::process::exit(1) skips destructors and violates the project rule against calling exit(). Replace with ExitCode return type which is the idiomatic Rust approach. Exit codes: 0=success, 1=partial failure, 2=fatal.
FFI structures belong in ntapi.rs per architecture rules. Extracted the CombinePhysicalMemoryInfo struct from cleaner::combine_memory() into ntapi.rs and added execute_combine_memory() safe wrapper, matching the existing execute_memory_command() pattern.
This is a Windows-only project by design. The cfg gate on enable_ansi_colors was dead code that added unnecessary nesting.
The RUNNING atomic bool is a simple flag with no associated data dependencies. Release/Acquire semantics correctly convey intent and avoid the unnecessary total ordering guarantee of SeqCst.
…matter Four kernel operations (empty_working_sets_kernel, flush_modified_list, purge_standby_low_priority, purge_standby_all) shared identical pattern: capture before → execute_memory_command → wait_for_settle → CleanResult. Extracted execute_kernel_memory_op() and ntstatus_error_message() helpers, reducing ~80 lines of duplication to parameterized calls.
The memory load color logic (red >85%, yellow >60%, green <=60%) was duplicated in print_physical_memory and print_compact_status. Extracted into a reusable coloured_load() function.
If the Ctrl+C handler fails to install, the monitor has no graceful shutdown mechanism. Bail early instead of running without shutdown capability.
interval_secs * 2 (cooldown) and interval_secs * 10 (sleep ticks) can silently overflow for large inputs, causing near-zero cooldowns or zero-iteration sleep loops. Use saturating_mul to clamp at u64::MAX.
Previously, flush_file_cache returned success even when the post-purge restore call (SetSystemFileCacheSize(0,0,0)) failed, leaving the system file cache in a degraded state. Now returns CleanResult::failure so callers are aware the system may need a reboot.
If smart_clean fails repeatedly, the monitor now aborts after 3 consecutive errors instead of looping indefinitely. Prevents infinite error-clean-error loops on malfunctioning systems where auto-clean cannot succeed.
buf[21] maps to ModifiedPageCountPageFile in the NT struct, which is the subset of modified pages destined for the pagefile, not the total of all modified pages. The old name was misleading.
The 1 MB jitter threshold was hardcoded, causing timeout on systems with 64+ GB RAM where normal kernel activity exceeds 1 MB between polls. Now uses max(4 MB, 0.01% of total physical RAM).
Add QuickMemoryReading struct that only calls GlobalMemoryStatusEx, skipping K32GetPerformanceInfo. Used in wait_for_settle to avoid redundant commit charge / kernel pool / handle count queries during the settle polling loop (up to 20 iterations at 150ms each).
Add SettleMode enum (Full/Quick) to control wait_for_settle behavior. Intermediate operations in smart_clean chains use Quick (1 stable read, 0.8s max) while the final operation uses Full (3 stable reads, 2s max). This reduces worst-case settle overhead on Nuclear clean from ~14s to ~4.8s. Extract execute_aggressive_chain and execute_nuclear_chain helpers to keep smart_clean under the 100-line limit.
Add MemoryListCommand::display_info() method that returns (name, success_msg, verbose_label) for each variant. Simplifies execute_kernel_memory_op from 6 params to 3 and eliminates duplicated string literals across public wrappers, chain helpers, and smart_clean dispatch.
The format string was only used in one place (execute_kernel_memory_op error path). Inlining it removes an unnecessary indirection.
Change local_now() output from HH:MM:SS to YYYY-MM-DD HH:MM:SS so long-running monitor sessions show which day each reading was taken.
Adds a global --no-color flag that disables ANSI colour codes via colored::control::set_override(false). Useful for piping output to files or non-ANSI terminals. Documents the flag in README.md Global Options section.
When --quiet is set the banner is hidden, verbose progress is forced off, and only results, errors, and machine-readable data are printed. Useful for scripting and automation workflows.
Enumerates running processes via Toolhelp32 and queries working set counters with K32GetProcessMemoryInfo. Displays a ranked table of the top N processes by physical RAM usage. Works with --json for machine-readable output.
Derives Serialize on CleanResult, CleanLevel, and SmartCleanResult so the cleaning output can be serialized. When --report <FILE> is passed, the full result (per-operation stats, before/after snapshots, total freed) is written as pretty-printed JSON.
Lists the operations that would execute for a given level without running any kernel commands. Includes unit test validating operation counts per level.
Replaces the hardcoded 2x interval cooldown with a user-configurable --cooldown <SECONDS> option. Defaults to 2x the check interval when not specified.
Allows users to protect specific processes from working set trimming by name. Case-insensitive matching with optional .exe suffix. Implies --per-process since kernel-level trim cannot exclude individual processes.
- Add elapsed_secs field to CleanResult for per-operation timing - Add total_elapsed_secs field to SmartCleanResult for overall timing - Track wall-clock time (including settle) in execute_kernel_memory_op, flush_file_cache_with_settle, empty_working_sets_per_process, and combine_memory_with_settle using std::time::Instant - Display per-operation elapsed time in print_single_result and print_clean_summary table rows - Display total elapsed time in cleaning summary footer - Update CleanResult::success() to accept Duration parameter - Update tests to pass Duration values and assert elapsed_secs
- Add SystemRegistryReconciliationInformation (class 155) constant to ntapi.rs - Add execute_registry_flush() NT API wrapper in ntapi.rs - Add flush_registry_cache() and flush_registry_cache_with_settle() in cleaner.rs - Add FlushRegistry subcommand to CLI with verbose flag - Wire FlushRegistry in main.rs dispatch - Include registry flush in aggressive chain (5 ops) and nuclear chain (8 ops) - Update dry_run_plan and operation count tests - Update README.md: ToC, subcommand docs, cleaning level tables, comparison - Update AFTER_HELP_LONG with flush-registry example and Registry Cache concept - Add section 7b to docs/RUST_IMPLEMENTATION_GUIDE.md
- Add SystemFileCacheInformation (class 21) constant and SystemFileCacheInfo FFI struct to ntapi.rs - Add FileCacheSnapshot struct with capture() method to stats.rs - Add print_file_cache() display function to display.rs - Update print_status() to accept and display optional file cache info - Show file cache current/peak/min/max in status --detailed output - Include file_cache in JSON output for status --json --detailed
- Add HandleGuard RAII wrappers in cleaner.rs and stats.rs to prevent handle leaks on panics - Add 3x retry with 50ms delays for file cache restore (CacheRestoreGuard::drop and explicit path) - Fix dry_run_plan to show per-process mode when --exclude is used - Harden MemoryListInfo::query to bail on zero return_length and limit buffer slice - Add 4 new NTSTATUS constants with descriptive error messages - Add INVALID_HANDLE_VALUE guard in enable_ansi_colors - Add 26 new tests across cleaner, stats, ntapi, and display modules (15 -> 41 total)
HandleGuard was identically defined in both cleaner.rs and stats.rs (~30 lines each). Consolidated into stats.rs with pub visibility so cleaner.rs imports it. Removes ~30 lines of exact duplicate code.
Replace 3 manual CloseHandle calls on error paths with HandleGuard. Eliminates risk of handle leaks if future edits add early returns.
Both cleaner.rs and stats.rs had identical Toolhelp32 snapshot boilerplate for process enumeration. Extracted enumerate_processes() into stats.rs. Cleaner.rs now delegates enumeration and no longer imports Toolhelp FFI types directly.
empty_working_sets_per_process always used SettleMode::Full, even as an intermediate step in aggressive/nuclear chains where Quick suffices. Added _with_settle variant following the same pattern as flush_file_cache. Saves ~1.2s on chains with process exclusions.
CombinePhysicalMemoryInfoEx had its first doc-comment line duplicated. MemoryListCommand's #[allow(dead_code)] lacked the required explanatory comment per project rules.
All four assertions were already covered by the individual format_bytes_kilobytes, format_bytes_megabytes, format_bytes_gigabytes, and format_bytes_terabytes tests.
The project policy requires doc comments on every public item, but the missing_docs lint was only set to warn — meaning violations never blocked a build. Promoting to deny ensures the compiler enforces the documented policy at build time. All public items already have doc comments, so no code changes are needed.
…ment Win32 SetConsoleCtrlHandler requires an extern "system" callback that cannot capture state, making the RUNNING AtomicBool inherently global. This was undocumented and unguarded against concurrent calls. Changes: - Add MONITOR_ACTIVE AtomicBool with compare_exchange to prevent concurrent run_monitor calls (would corrupt shared RUNNING flag) - Add RAII MonitorGuard that clears MONITOR_ACTIVE on all exit paths - Add comprehensive module-level docs explaining why global state is required (Win32 API limitation, not a design choice) - Extract handle_threshold_clean helper to keep run_monitor under the 100-line function limit - Move MAX_CONSECUTIVE_ERRORS to module-level const
Previously, --no-color was processed AFTER Cli::parse(), but clap's --help handler renders and exits DURING parsing. This meant help text containing raw ANSI escape codes (\x1b[...] in LONG_ABOUT, AFTER_HELP, etc.) was never stripped when --no-color was passed. Fix: pre-scan argv and NO_COLOR env var BEFORE clap parsing, then set ColorChoice::Never on the Command so clap strips all embedded ANSI codes from StyledStr help output. Also: - Properly wire NO_COLOR env var to colored crate override - Skip ANSI console initialization when color is disabled - Update --no-color doc comment to mention help text coverage - Extract detect_no_color() and parse_cli() helpers
Restructure from binary-only to lib+bin layout so criterion can import modules for benchmarking. Create src/lib.rs as the library crate root with pub module declarations, update src/main.rs to import from the library. Add 5 benchmark groups (17 benchmarks total) covering hot paths: - format_bytes: 7 magnitude inputs (0B through 1TB) - extract_exe_name: short/long/full-buffer process names - ntstatus_message: success/known-error/unknown status codes - memory_capture: full MemorySnapshot vs lightweight QuickMemoryReading - snapshot_calculations: commit_percent and total_standby_pages Supporting changes: - Add doc comments to all pub struct fields and constants (missing_docs deny) - Add #[must_use] to pure public functions (must_use_candidate) - Make nt_query_system_information unsafe fn (not_unsafe_ptr_arg_deref) - Allow missing_errors_doc/missing_panics_doc in Cargo.toml (binary crate) - Fix too_long_first_doc_paragraph on MemoryListInfo
The dry_run_plan labeled second-pass operations as 'Flush Modified List (2nd pass)' and 'Purge ALL Standby (2nd pass)', but actual execution produced CleanResult entries with identical names as the first pass. This caused a confusing mismatch between --dry-run preview and real cleaning output. Now appends '(2nd pass)' to the operation field of CleanResult for both second-pass operations in execute_nuclear_chain, so summary output matches the dry-run labels exactly.
The test comment claimed '30 ASCII chars' but the actual truncation produces 29 chars (boundary = max_len - 1 via take_while(i < max_len), then name[..boundary] = boundary bytes). The assertion bound of <= 34 was also unnecessarily loose. Fixed: comment now says '29 ASCII chars + … (3 bytes UTF-8)' and the bound is tightened from <= 34 to <= 32 (the correct maximum).
Four rustdoc errors blocked the pre-push documentation build: - cleaner.rs: smart_clean doc linked to private SettleMode::Quick and SettleMode::Full. Private items cannot be linked from public doc comments; replaced with backtick code spans. - monitor.rs: module-level doc linked to run_monitor (unresolvable in module-doc context) and private MonitorGuard. Replaced both with backtick code spans.
Normalise all text files to LF in the repository. On checkout: - LF for all source files (.rs, .toml, .lock), docs, configs, and Unix-style git hooks - CRLF for Windows-native scripts (.bat, .cmd, .ps1) so they run without modification on Windows - Binary marking for asset files (.ico, .exe, .dll, etc.) to prevent diff/merge corruption This ensures cross-platform code compatibility — anyone cloning on Linux/macOS gets LF everywhere, Windows users get CRLF only where the OS requires it.
Wire context_menu.rs module with install/uninstall CLI subcommands. - Add context-menu install/uninstall commands to CLI (cli.rs, main.rs) - Embed all 3 icons (app.ico, lite.ico, aggressive.ico) as Win32 resources in build.rs - Use negative resource ID format (exe,-N) for reliable icon referencing - Add Win32_System_Registry feature to windows-sys dependency - Declare context_menu module in lib.rs with #[allow(unsafe_code)] - Add context menu examples to AFTER_HELP_LONG and LONG_ABOUT feature list - Update README.md with context-menu command docs, FAQ, and architecture - Update RUST_IMPLEMENTATION_GUIDE.md and copilot-instructions.md
Add --notify hidden CLI flag that hides the console window via FreeConsole(), runs the operation silently, and shows a balloon notification with results using Shell_NotifyIconW. The notification auto-dismisses after 2 seconds and is not saved in Windows Action Center (uses NOTIFYICON_VERSION v3 + NIF_REALTIME). - Add hide_console_window() and show_balloon_notification() to console.rs - Add --notify flag to Cli struct (hidden, global) - Wire --notify in main.rs: detect before clap, hide console, format results - Update context_menu.rs entries from --quiet to --notify - Add Win32_UI_Shell, Win32_UI_WindowsAndMessaging, Win32_System_LibraryLoader features - Update README.md and RUST_IMPLEMENTATION_GUIDE.md
Change PE subsystem from CONSOLE to WINDOWS so the OS never auto-creates a console window. In --notify mode (context menu), no terminal appears at all. For CLI usage, dynamically attach to the parent console via AttachConsole or allocate a new one via AllocConsole for double-click launches. - Add #![windows_subsystem = "windows"] to main.rs - Replace hide_console_window()/FreeConsole with attach_or_create_console() - Add redirect_std_handles() to open CONOUT$/CONIN$ after AttachConsole - Add ConsoleMode enum (Attached/Allocated) for standalone detection - Add Win32_Storage_FileSystem feature for CreateFileW - Update all docs: copilot-instructions, README, impl guide, cli.rs
Two fixes for the SUBSYSTEM:WINDOWS migration: - Notification: Shell_NotifyIconW needs a valid hWnd (not null) to deliver callback messages. Create a hidden message-only window (HWND_MESSAGE) and run a Win32 message pump during the balloon display period. - Terminal exit: only pause_before_exit when the user double-clicked the bare exe (AllocConsole + no CLI args). Prevents the UAC-from-terminal scenario where the pause blocks a separate console window while the original terminal appears stuck.
…haviour SUBSYSTEM:WINDOWS made PowerShell/cmd not wait for the process, causing the terminal to appear stuck after output. Switch back to SUBSYSTEM:CONSOLE so the terminal works normally. For context-menu (--notify) launches, immediately hide and FreeConsole() the auto-created console window before it renders. Uses GetConsoleProcessList for standalone detection instead of AttachConsole/AllocConsole. Removes redirect_std_handles (no longer needed).
…ext_menu Rustdoc cannot resolve links to private items (hide_and_free_console, ENTRIES). Replace link brackets with plain backtick code formatting.
Previously, print_clean_summary showed 'Net change: 500.00 MB' without a minus sign when total_freed was negative, misleading users into thinking memory was freed when it actually decreased. Also used the same message for zero and negative cases. Now uses a three-way match: - Positive: 'Total freed: X' (green, star icon) - Zero: 'Net change: 0 B (already clean)' - Negative: 'Net change: -X (pages re-faulted during clean)'
to_wide() was identically defined in both context_menu.rs and privilege.rs. Extract to stats.rs (which already hosts format_bytes and extract_exe_name) as a public utility, and update both callers to import crate::stats::to_wide.
The module doc showed 'MagicXRAMCleaner' in the registry layout diagram but the actual key is 'zMagicXRAMCleaner' (z prefix pushes the entry to the bottom of the context menu alphabetically). Also tightened root_paths_end_with_expected_key_name test to check for the exact z-prefixed name — previously it only checked for the suffix 'MagicXRAMCleaner' which would pass even if the z prefix was removed.
Replace bare 0x01 | 0x10 in dwInfoFlags with named constants NIIF_INFO and NIIF_NOSOUND. These shellapi.h constants are not exposed by windows-sys, so they are defined locally with doc comments referencing their origin.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Documentation updated
Key architectural improvements
Type of Change
Related Issues
Checklist
cargo fmt— code is formattedcargo clippy— no lint errors (deny level)cargo test— all tests passcargo build --release— release build succeedscargo doc— documentation builds without warningsTesting
Screenshots / Output