From 6b572b1ab4de488f933c2c9fad280d2e9490f9d7 Mon Sep 17 00:00:00 2001 From: nyx Date: Tue, 11 Feb 2025 11:16:05 -0500 Subject: [PATCH] refactor: misc restructering (#1015) Changes: Separate cli flags and put them in their own module. Add a rustfmt configuration file for grouping imports into crates (so we dont have to do this manually, and it seems we were already doing it manually so might as well) Use comments to describe cli flags instead of using ugly syntax (they both work the same but one is less ugly and more readable) Add a --mouse flag to enable mouse interaction (mouse interaction is now off by default) Add a --bypass-root flag to disable the annoying popup when you run the utility as root Fix an issue where you can't reach the bottom of the list in a subdir (i also reduced the nesting in those functions as well for readability) Add help feature to clap dependency to enable --help / -h Add usage feature to clap dependency to enable usage example when running with --help / -h Remove CLI arg examples from readme, and add instructions on how to view them on CLI to prevent it from bloating up the readme --- README.md | 23 ++++++------------ core/src/inner.rs | 2 +- rustfmt.toml | 1 + tui/Cargo.toml | 2 +- tui/src/cli.rs | 35 +++++++++++++++++++++++++++ tui/src/main.rs | 38 +++++++---------------------- tui/src/root.rs | 8 ++++-- tui/src/state.rs | 59 +++++++++++++++++++++++++++++++++------------ xtask/src/docgen.rs | 3 +-- xtask/src/main.rs | 7 +++--- 10 files changed, 109 insertions(+), 69 deletions(-) create mode 100644 rustfmt.toml create mode 100644 tui/src/cli.rs diff --git a/README.md b/README.md index c0568171..a614f4bc 100644 --- a/README.md +++ b/README.md @@ -27,25 +27,18 @@ curl -fsSL https://christitus.com/linuxdev | sh ### CLI arguments -Linutil supports various command-line arguments to customize its behavior. Here are some common arguments you can use: - -- `-c, --config ` : Path to the configuration file. -- `--override-validation` : Show all available options, disregarding compatibility checks (UNSAFE). -- `--size-bypass` : Bypass the terminal size limit. -- `-y, --skip-confirmation` : Skip confirmation prompt before executing commands. -- `-t, --theme ` : Set the theme to use in the application [default: `default`] [possible values: `default`, `compatible`]. -- `-h, --help` : Print help. - -For more detailed usage, run: - -```bash -curl -fsSL https://christitus.com/linux | sh -s -- --help -``` +View available options by running: ```bash linutil --help ``` +For installer options: + +```bash +curl -fsSL https://christitus.com/linux | sh -s -- --help +``` + ## ⬇️ Installation Linutil is also available as a package in various repositories: @@ -142,7 +135,7 @@ Docs are now [here](https://github.com/Chris-Titus-Docs/linutil-docs) ## 🏅 Thanks to All Contributors -Thank you to everyone who has contributed to the development of Linutil. Your efforts are greatly appreciated, and you’re helping make this tool better for everyone! +Thank you to everyone who has contributed to the development of Linutil. Your efforts are greatly appreciated, and you're helping make this tool better for everyone! [![Contributors](https://contrib.rocks/image?repo=ChrisTitusTech/linutil)](https://github.com/ChrisTitusTech/linutil/graphs/contributors) diff --git a/core/src/inner.rs b/core/src/inner.rs index 16d12cbe..8621ea2b 100644 --- a/core/src/inner.rs +++ b/core/src/inner.rs @@ -120,7 +120,7 @@ enum EntryType { impl Entry { fn is_supported(&self) -> bool { - self.preconditions.as_deref().map_or(true, |preconditions| { + self.preconditions.as_deref().is_none_or(|preconditions| { preconditions.iter().all( |Precondition { matches, diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 00000000..d9ba5fdb --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +imports_granularity = "Crate" \ No newline at end of file diff --git a/tui/Cargo.toml b/tui/Cargo.toml index 9143baf8..1041fc29 100644 --- a/tui/Cargo.toml +++ b/tui/Cargo.toml @@ -14,7 +14,7 @@ default = ["tips"] tips = ["rand"] [dependencies] -clap = { version = "4.5.20", features = ["derive", "std"], default-features = false } +clap = { version = "4.5.20", features = ["derive", "std", "help", "usage"], default-features = false } oneshot = { version = "0.1.8", features = ["std"], default-features = false } portable-pty = "0.8.1" ratatui = { version = "0.29.0", features = ["crossterm"], default-features = false } diff --git a/tui/src/cli.rs b/tui/src/cli.rs new file mode 100644 index 00000000..564b2d68 --- /dev/null +++ b/tui/src/cli.rs @@ -0,0 +1,35 @@ +use crate::theme::Theme; +use clap::Parser; +use std::path::PathBuf; + +#[derive(Debug, Parser, Clone)] +pub struct Args { + /// Path to the configuration file + #[arg(short, long)] + pub config: Option, + + /// Set the theme to use in the application + #[arg(short, long, value_enum)] + #[arg(default_value_t = Theme::Default)] + pub theme: Theme, + + /// Skip confirmation prompt before executing commands + #[arg(short = 'y', long)] + pub skip_confirmation: bool, + + /// Show all available options, disregarding compatibility checks (UNSAFE) + #[arg(short = 'u', long)] + pub override_validation: bool, + + /// Bypass the terminal size limit + #[arg(short = 's', long)] + pub size_bypass: bool, + + /// Enable mouse interaction + #[arg(short = 'm', long)] + pub mouse: bool, + + /// Bypass root user check + #[arg(short = 'r', long)] + pub bypass_root: bool, +} diff --git a/tui/src/main.rs b/tui/src/main.rs index 76ef584f..11ace649 100644 --- a/tui/src/main.rs +++ b/tui/src/main.rs @@ -1,3 +1,4 @@ +mod cli; mod confirmation; mod filter; mod float; @@ -11,7 +12,7 @@ mod theme; #[cfg(feature = "tips")] mod tips; -use crate::theme::Theme; +use crate::cli::Args; use clap::Parser; use ratatui::{ backend::CrosstermBackend, @@ -26,40 +27,17 @@ use ratatui::{ use state::AppState; use std::{ io::{stdout, Result, Stdout}, - path::PathBuf, time::Duration, }; -// Linux utility toolbox -#[derive(Debug, Parser)] -pub struct Args { - #[arg(short, long, help = "Path to the configuration file")] - config: Option, - #[arg(short, long, value_enum)] - #[arg(default_value_t = Theme::Default)] - #[arg(help = "Set the theme to use in the application")] - theme: Theme, - #[arg( - short = 'y', - long, - help = "Skip confirmation prompt before executing commands" - )] - skip_confirmation: bool, - #[arg(long, default_value_t = false)] - #[clap(help = "Show all available options, disregarding compatibility checks (UNSAFE)")] - override_validation: bool, - #[arg(long, default_value_t = false)] - #[clap(help = "Bypass the terminal size limit")] - size_bypass: bool, -} - fn main() -> Result<()> { let args = Args::parse(); - - let mut state = AppState::new(args); + let mut state = AppState::new(args.clone()); stdout().execute(EnterAlternateScreen)?; - stdout().execute(EnableMouseCapture)?; + if args.mouse { + stdout().execute(EnableMouseCapture)?; + } enable_raw_mode()?; let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; @@ -70,7 +48,9 @@ fn main() -> Result<()> { // restore terminal disable_raw_mode()?; terminal.backend_mut().execute(LeaveAlternateScreen)?; - terminal.backend_mut().execute(DisableMouseCapture)?; + if args.mouse { + terminal.backend_mut().execute(DisableMouseCapture)?; + } terminal.backend_mut().execute(ResetColor)?; terminal.show_cursor()?; diff --git a/tui/src/root.rs b/tui/src/root.rs index 46432d06..23468ef2 100644 --- a/tui/src/root.rs +++ b/tui/src/root.rs @@ -8,8 +8,12 @@ This means you have full system access and commands can potentially damage your Please proceed with caution and make sure you understand what each script does before executing it."; #[cfg(unix)] -pub fn check_root_status<'a>() -> Option> { - (Uid::effective().is_root()).then_some(FloatingText::new( +pub fn check_root_status(bypass_root: bool) -> Option> { + if bypass_root { + return None; + } + + Uid::effective().is_root().then_some(FloatingText::new( ROOT_WARNING.into(), "Root User Warning", true, diff --git a/tui/src/state.rs b/tui/src/state.rs index 4bcb9c48..3baffef3 100644 --- a/tui/src/state.rs +++ b/tui/src/state.rs @@ -66,6 +66,7 @@ pub struct AppState { tip: &'static str, size_bypass: bool, skip_confirmation: bool, + mouse_enabled: bool, } pub enum Focus { @@ -94,8 +95,18 @@ enum SelectedItem { None, } +enum ScrollDir { + Up, + Down, +} + impl AppState { pub fn new(args: Args) -> Self { + #[cfg(unix)] + let root_warning = check_root_status(args.bypass_root); + #[cfg(not(unix))] + let root_warning = None; + let tabs = linutil_core::get_tabs(!args.override_validation); let root_id = tabs[0].tree.root().id(); @@ -122,10 +133,11 @@ impl AppState { tip: crate::tips::get_random_tip(), size_bypass: args.size_bypass, skip_confirmation: args.skip_confirmation, + mouse_enabled: args.mouse, }; #[cfg(unix)] - if let Some(root_warning) = check_root_status() { + if let Some(root_warning) = root_warning { state.spawn_float(root_warning, FLOAT_SIZE, FLOAT_SIZE); } @@ -440,6 +452,10 @@ impl AppState { } pub fn handle_mouse(&mut self, event: &MouseEvent) -> bool { + if !self.mouse_enabled { + return true; + } + if !self.drawable { return true; } @@ -597,24 +613,35 @@ impl AppState { true } - fn scroll_down(&mut self) { - if let Some(selected) = self.selection.selected() { - if selected == self.filter.item_list().len() - 1 { - self.selection.select_first(); - } else { - self.selection.select_next(); - } - } + fn scroll(&mut self, direction: ScrollDir) { + let Some(selected) = self.selection.selected() else { + return; + }; + let list_len = if !self.at_root() { + self.filter.item_list().len() + 1 + } else { + self.filter.item_list().len() + }; + + if list_len == 0 { + return; + }; + + let next_selection = match direction { + ScrollDir::Up if selected == 0 => list_len - 1, + ScrollDir::Down if selected >= list_len - 1 => 0, + ScrollDir::Up => selected - 1, + ScrollDir::Down => selected + 1, + }; + self.selection.select(Some(next_selection)); } fn scroll_up(&mut self) { - if let Some(selected) = self.selection.selected() { - if selected == 0 { - self.selection.select_last(); - } else { - self.selection.select_previous(); - } - } + self.scroll(ScrollDir::Up) + } + + fn scroll_down(&mut self) { + self.scroll(ScrollDir::Down) } fn toggle_multi_select(&mut self) { diff --git a/xtask/src/docgen.rs b/xtask/src/docgen.rs index 385c1134..15a5e2af 100644 --- a/xtask/src/docgen.rs +++ b/xtask/src/docgen.rs @@ -2,8 +2,7 @@ use std::fs; use linutil_core::Command; -use crate::path; -use crate::DynError; +use crate::{path, DynError}; pub const USER_GUIDE: &str = "userguide.md"; diff --git a/xtask/src/main.rs b/xtask/src/main.rs index fecab8c8..a1be3f7c 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -6,9 +6,10 @@ use std::{env, error::Error}; type DynError = Box; pub mod tasks { - use crate::docgen::USER_GUIDE; - use crate::docgen::{userguide, write}; - use crate::DynError; + use crate::{ + docgen::{userguide, write, USER_GUIDE}, + DynError, + }; pub fn docgen() -> Result<(), DynError> { write(USER_GUIDE, &userguide()?);