Skip to content

refactor: implement modular architecture - #1

Merged
ehsan18t merged 69 commits into
mainfrom
refactor/modular-architecture
Feb 18, 2026
Merged

refactor: implement modular architecture#1
ehsan18t merged 69 commits into
mainfrom
refactor/modular-architecture

Conversation

@ehsan18t

@ehsan18t ehsan18t commented Feb 17, 2026

Copy link
Copy Markdown
Owner

Description

Documentation updated

  • copilot-instructions.md - Architecture file tree, help text location, quick-reference steps.
  • README.md - Technical Architecture file tree.

Key architectural improvements

  • Single Responsibility - each module has one clear job.
  • Data over printing - smart_clean() returns a SmartCleanResult struct instead of printing directly, enabling easier testing and reuse.
  • Display consolidation - all terminal formatting lives in display.rs.
  • Thin entry point - main.rs is pure orchestration, no business logic or formatting.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Refactor (no functional changes)
  • Documentation update
  • CI / build change

Related Issues

  • N/A

Checklist

  • cargo fmt — code is formatted
  • cargo clippy — no lint errors (deny level)
  • cargo test — all tests pass
  • cargo build --release — release build succeeds
  • cargo doc — documentation builds without warnings
  • Documentation updated (README, help text, guides) if behaviour changed
  • Commit messages follow Conventional Commits format

Testing

  • N/A

Screenshots / Output

  • N/A

- 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.
@ehsan18t
ehsan18t merged commit 51b8bb7 into main Feb 18, 2026
2 checks passed
@ehsan18t
ehsan18t deleted the refactor/modular-architecture branch February 18, 2026 05:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant