mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-21 21:09:42 +00:00
Merge pull request #153 from lj3954/simple_additions
feat: Use TOML data to add tabs to UI.
This commit is contained in:
commit
5b2f5f20b2
153
Cargo.lock
generated
153
Cargo.lock
generated
|
@ -269,6 +269,22 @@ version = "1.12.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filedescriptor"
|
||||
version = "0.8.2"
|
||||
|
@ -302,6 +318,15 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.60"
|
||||
|
@ -344,6 +369,16 @@ dependencies = [
|
|||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ioctl-rs"
|
||||
version = "0.1.6"
|
||||
|
@ -395,6 +430,12 @@ version = "0.2.155"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
|
@ -420,6 +461,12 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
|
@ -626,6 +673,19 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.17"
|
||||
|
@ -644,6 +704,35 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.205"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.205"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial"
|
||||
version = "0.4.0"
|
||||
|
@ -832,6 +921,40 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tui"
|
||||
version = "0.1.0"
|
||||
|
@ -844,8 +967,11 @@ dependencies = [
|
|||
"oneshot",
|
||||
"portable-pty",
|
||||
"ratatui",
|
||||
"serde",
|
||||
"tempdir",
|
||||
"toml",
|
||||
"tui-term",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -992,6 +1118,18 @@ version = "0.2.92"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "6.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d9c5ed668ee1f17edb3b627225343d210006a90bb1e3745ce1f30b1fb115075"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"rustix",
|
||||
"winsafe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
@ -1162,6 +1300,15 @@ version = "0.52.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
|
@ -1171,6 +1318,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winsafe"
|
||||
version = "0.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.34"
|
||||
|
|
|
@ -14,6 +14,9 @@ ratatui = "0.27.0"
|
|||
tui-term = "0.1.12"
|
||||
include_dir = "0.7.4"
|
||||
tempdir = "0.3.7"
|
||||
serde = { version = "1.0.205", features = ["derive"] }
|
||||
toml = "0.8.19"
|
||||
which = "6.0.2"
|
||||
|
||||
[[bin]]
|
||||
name = "linutil"
|
||||
|
|
4
build.rs
Normal file
4
build.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
fn main() {
|
||||
// Rebuild program if any file in commands directory changes.
|
||||
println!("cargo:rerun-if-changed=src/commands");
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. ./common-script.sh
|
||||
. ../common-script.sh
|
||||
|
||||
setupAlacritty() {
|
||||
echo "Install Alacritty if not already installed..."
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/sh -e
|
||||
. ./common-script.sh
|
||||
. ../common-script.sh
|
||||
|
||||
makeDWM(){
|
||||
cd $HOME && git clone https://github.com/ChrisTitusTech/dwm-titus.git # CD to Home directory to install dwm-titus
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. ./common-script.sh
|
||||
. ../common-script.sh
|
||||
|
||||
setupKitty() {
|
||||
echo "Install Kitty if not already installed..."
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. ./common-script.sh
|
||||
. ../common-script.sh
|
||||
|
||||
setupRofi() {
|
||||
echo "Install Rofi if not already installed..."
|
||||
|
|
29
src/commands/applications-setup/tab_data.toml
Normal file
29
src/commands/applications-setup/tab_data.toml
Normal file
|
@ -0,0 +1,29 @@
|
|||
name = "Applications Setup"
|
||||
|
||||
[[data]]
|
||||
name = "Alacritty"
|
||||
script = "alacritty-setup.sh"
|
||||
|
||||
[[data]]
|
||||
name = "Bash Prompt"
|
||||
command = "bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/mybash/main/setup.sh)\""
|
||||
|
||||
[[data]]
|
||||
name = "DWM-Titus"
|
||||
script = "dwmtitus-setup.sh"
|
||||
|
||||
[[data]]
|
||||
name = "Kitty"
|
||||
script = "kitty-setup.sh"
|
||||
|
||||
[[data]]
|
||||
name = "Neovim"
|
||||
command = "bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/neovim/main/setup.sh)\""
|
||||
|
||||
[[data]]
|
||||
name = "Rofi"
|
||||
script = "rofi-setup.sh"
|
||||
|
||||
[[data]]
|
||||
name = "ZSH Prompt"
|
||||
script = "zsh-setup.sh"
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
. ./common-script.sh
|
||||
. ../common-script.sh
|
||||
|
||||
# Function to install zsh
|
||||
install_zsh() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. ./common-script.sh
|
||||
. ../common-script.sh
|
||||
|
||||
installPkg() {
|
||||
echo "Install UFW if not already installed..."
|
||||
|
|
5
src/commands/security/tab_data.toml
Normal file
5
src/commands/security/tab_data.toml
Normal file
|
@ -0,0 +1,5 @@
|
|||
name = "Security"
|
||||
|
||||
[[data]]
|
||||
name = "Firewall Baselines (CTT)"
|
||||
script = "firewall-baselines.sh"
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. ./common-script.sh
|
||||
. ../common-script.sh
|
||||
|
||||
# Check if the home directory and linuxtoolbox folder exist, create them if they don't
|
||||
LINUXTOOLBOXDIR="$HOME/linuxtoolbox"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. ./common-script.sh
|
||||
. ../common-script.sh
|
||||
|
||||
installDepend() {
|
||||
## Check for dependencies.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. ./common-script.sh
|
||||
. ../common-script.sh
|
||||
|
||||
# Check if the home directory and linuxtoolbox folder exist, create them if they don't
|
||||
LINUXTOOLBOXDIR="$HOME/linuxtoolbox"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. ./common-script.sh
|
||||
. ../common-script.sh
|
||||
|
||||
removeSnaps() {
|
||||
case $PACKAGER in
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. ./common-script.sh
|
||||
. ../common-script.sh
|
||||
|
||||
fastUpdate() {
|
||||
case ${PACKAGER} in
|
37
src/commands/system-setup/tab_data.toml
Normal file
37
src/commands/system-setup/tab_data.toml
Normal file
|
@ -0,0 +1,37 @@
|
|||
name = "System Setup"
|
||||
|
||||
[[data]]
|
||||
name = "Arch Linux"
|
||||
|
||||
[[data.preconditions]]
|
||||
matches = true
|
||||
data = "command_exists"
|
||||
values = ["pacman"]
|
||||
|
||||
[[data.entries]]
|
||||
name = "Yay AUR Helper"
|
||||
script = "arch/yay-setup.sh"
|
||||
|
||||
[[data.entries]]
|
||||
name = "Paru AUR Helper"
|
||||
script = "arch/paru-setup.sh"
|
||||
|
||||
[[data]]
|
||||
name = "Full System Update"
|
||||
script = "system-update.sh"
|
||||
|
||||
[[data]]
|
||||
name = "Build Prerequisites"
|
||||
script = "1-compile-setup.sh"
|
||||
|
||||
[[data]]
|
||||
name = "Gaming Dependencies"
|
||||
script = "2-gaming-setup.sh"
|
||||
|
||||
[[data]]
|
||||
name = "Global Theme"
|
||||
script = "3-global-theme.sh"
|
||||
|
||||
[[data]]
|
||||
name = "Remove Snaps"
|
||||
script = "4-remove-snaps.sh"
|
1
src/commands/tabs.toml
Normal file
1
src/commands/tabs.toml
Normal file
|
@ -0,0 +1 @@
|
|||
directories = ["system-setup", "applications-setup", "security", "utils"]
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
source ./utils/monitor-control/utility_functions.sh
|
||||
source ./utility_functions.sh
|
||||
|
||||
# Function to auto-detect displays and set common resolution
|
||||
auto_detect_displays() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
source ./utils/monitor-control/utility_functions.sh
|
||||
source ./utility_functions.sh
|
||||
|
||||
# Function to change monitor orientation
|
||||
change_orientation() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
source ./utils/monitor-control/utility_functions.sh
|
||||
source ./utility_functions.sh
|
||||
|
||||
RESET='\033[0m'
|
||||
BOLD='\033[1m'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
source ./utils/monitor-control/utility_functions.sh
|
||||
source ./utility_functions.sh
|
||||
|
||||
# Function to duplicate displays
|
||||
duplicate_displays() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
source ./utils/monitor-control/utility_functions.sh
|
||||
source ./utility_functions.sh
|
||||
|
||||
RESET='\033[0m'
|
||||
BOLD='\033[1m'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
source ./utils/monitor-control/utility_functions.sh
|
||||
source ./utility_functions.sh
|
||||
|
||||
# Function to extend displays
|
||||
extend_displays() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
source ./utils/monitor-control/utility_functions.sh
|
||||
source ./utility_functions.sh
|
||||
|
||||
# Function to manage monitor arrangement
|
||||
manage_arrangement() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
source ./utils/monitor-control/utility_functions.sh
|
||||
source ./utility_functions.sh
|
||||
|
||||
# Function to reset scaling back to 1 (native resolution) for all monitors
|
||||
reset_scaling() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
source ./utils/monitor-control/utility_functions.sh
|
||||
source ./utility_functions.sh
|
||||
|
||||
# Function to scale smaller monitors to the highest resolution of a bigger monitor
|
||||
scale_monitors() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
source ./utils/monitor-control/utility_functions.sh
|
||||
source ./utility_functions.sh
|
||||
|
||||
# Function to set a monitor as primary
|
||||
set_primary_monitor() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
source ./utils/monitor-control/utility_functions.sh
|
||||
source ./utility_functions.sh
|
||||
|
||||
RESET='\033[0m'
|
||||
BOLD='\033[1m'
|
||||
|
|
66
src/commands/utils/tab_data.toml
Normal file
66
src/commands/utils/tab_data.toml
Normal file
|
@ -0,0 +1,66 @@
|
|||
name = "Utilities"
|
||||
|
||||
[[data]]
|
||||
name = "WiFi Manager"
|
||||
script = "wifi-control.sh"
|
||||
|
||||
[[data]]
|
||||
name = "Bluetooth Manager"
|
||||
script = "bluetooth-control.sh"
|
||||
|
||||
[[data]]
|
||||
name = "Monitor Control"
|
||||
|
||||
[[data.preconditions]]
|
||||
matches = true
|
||||
data = { environment = "XDG_SESSION_TYPE" }
|
||||
values = ["x11"]
|
||||
|
||||
[[data.entries]]
|
||||
name = "Set Resolution"
|
||||
script = "monitor-control/set_resolutions.sh"
|
||||
|
||||
[[data.entries]]
|
||||
name = "Set Resolution"
|
||||
script = "monitor-control/set_resolutions.sh"
|
||||
|
||||
[[data.entries]]
|
||||
name = "Duplicate Displays"
|
||||
script = "monitor-control/duplicate_displays.sh"
|
||||
|
||||
[[data.entries]]
|
||||
name = "Extend Displays"
|
||||
script = "monitor-control/extend_displays.sh"
|
||||
|
||||
[[data.entries]]
|
||||
name = "Auto Detect Displays"
|
||||
script = "monitor-control/auto_detect_displays.sh"
|
||||
|
||||
[[data.entries]]
|
||||
name = "Enable Monitor"
|
||||
script = "monitor-control/enable_monitor.sh"
|
||||
|
||||
[[data.entries]]
|
||||
name = "Disable Monitor"
|
||||
script = "monitor-control/disable_monitor.sh"
|
||||
|
||||
[[data.entries]]
|
||||
name = "Set Primary Monitor"
|
||||
script = "monitor-control/set_primary_monitor.sh"
|
||||
|
||||
[[data.entries]]
|
||||
name = "Change Orientation"
|
||||
script = "monitor-control/change_orientation.sh"
|
||||
|
||||
[[data.entries]]
|
||||
name = "Manage Arrangement"
|
||||
script = "monitor-control/manage_arrangement.sh"
|
||||
|
||||
[[data.entries]]
|
||||
name = "Scale Monitors"
|
||||
script = "monitor-control/scale_monitor.sh"
|
||||
|
||||
[[data.entries]]
|
||||
name = "Reset Scaling"
|
||||
script = "monitor-control/reset_scaling.sh"
|
||||
matches = true
|
|
@ -7,7 +7,6 @@ use ratatui::{
|
|||
widgets::{Block, Borders, List},
|
||||
Frame,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct FloatingText {
|
||||
text: Vec<String>,
|
||||
|
@ -19,7 +18,7 @@ impl FloatingText {
|
|||
Self { text, scroll: 0 }
|
||||
}
|
||||
|
||||
pub fn from_command(command: &Command, mut full_path: PathBuf) -> Option<Self> {
|
||||
pub fn from_command(command: &Command) -> Option<Self> {
|
||||
let lines = match command {
|
||||
Command::Raw(cmd) => {
|
||||
// Reconstruct the line breaks and file formatting after the
|
||||
|
@ -27,9 +26,8 @@ impl FloatingText {
|
|||
cmd.lines().map(|line| line.to_string()).collect()
|
||||
}
|
||||
Command::LocalFile(file_path) => {
|
||||
full_path.push(file_path);
|
||||
let file_contents = std::fs::read_to_string(&full_path)
|
||||
.map_err(|_| format!("File not found: {:?}", &full_path))
|
||||
let file_contents = std::fs::read_to_string(file_path)
|
||||
.map_err(|_| format!("File not found: {:?}", file_path))
|
||||
.unwrap();
|
||||
file_contents.lines().map(|line| line.to_string()).collect()
|
||||
}
|
||||
|
|
|
@ -34,20 +34,21 @@ struct Args {
|
|||
#[arg(default_value_t = Theme::Default)]
|
||||
#[arg(help = "Set the theme to use in the application")]
|
||||
theme: Theme,
|
||||
#[arg(long, default_value_t = false)]
|
||||
#[clap(help = "Show all available options, disregarding compatibility checks (UNSAFE)")]
|
||||
override_validation: bool,
|
||||
}
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
let theme = args.theme;
|
||||
|
||||
let commands_dir = include_dir!("src/commands");
|
||||
let temp_dir: TempDir = TempDir::new("linutil_scripts").unwrap();
|
||||
commands_dir
|
||||
.extract(temp_dir.path())
|
||||
.expect("Failed to extract the saved directory");
|
||||
|
||||
let mut state = AppState::new(theme, temp_dir.path().to_owned());
|
||||
let mut state = AppState::new(args.theme, temp_dir.path(), args.override_validation);
|
||||
|
||||
stdout().execute(EnterAlternateScreen)?;
|
||||
enable_raw_mode()?;
|
||||
|
|
|
@ -13,7 +13,7 @@ use ratatui::{
|
|||
};
|
||||
use std::{
|
||||
io::Write,
|
||||
path::Path,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
thread::JoinHandle,
|
||||
};
|
||||
|
@ -22,10 +22,10 @@ use tui_term::{
|
|||
widget::PseudoTerminal,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
pub enum Command {
|
||||
Raw(&'static str),
|
||||
LocalFile(&'static str),
|
||||
Raw(String),
|
||||
LocalFile(PathBuf),
|
||||
None, // Directory
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,7 @@ impl FloatContent for RunningCommand {
|
|||
}
|
||||
|
||||
impl RunningCommand {
|
||||
pub fn new(command: Command, temp_path: &Path) -> Self {
|
||||
pub fn new(command: Command) -> Self {
|
||||
let pty_system = NativePtySystem::default();
|
||||
|
||||
// Build the command based on the provided Command enum variant
|
||||
|
@ -137,13 +137,14 @@ impl RunningCommand {
|
|||
cmd.arg(prompt);
|
||||
}
|
||||
Command::LocalFile(file) => {
|
||||
cmd.arg(file);
|
||||
cmd.arg(&file);
|
||||
if let Some(parent) = file.parent() {
|
||||
cmd.cwd(parent);
|
||||
}
|
||||
}
|
||||
Command::None => panic!("Command::None was treated as a command"),
|
||||
}
|
||||
|
||||
cmd.cwd(temp_path);
|
||||
|
||||
// Open a pseudo-terminal with initial size
|
||||
let pair = pty_system
|
||||
.openpty(PtySize {
|
||||
|
|
43
src/state.rs
43
src/state.rs
|
@ -2,7 +2,7 @@ use crate::{
|
|||
float::{Float, FloatContent},
|
||||
floating_text::FloatingText,
|
||||
running_command::{Command, RunningCommand},
|
||||
tabs::{ListNode, TABS},
|
||||
tabs::{ListNode, Tab},
|
||||
theme::Theme,
|
||||
};
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
|
||||
|
@ -14,15 +14,15 @@ use ratatui::{
|
|||
widgets::{Block, Borders, List, ListState, Paragraph},
|
||||
Frame,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
use std::path::Path;
|
||||
|
||||
pub struct AppState {
|
||||
/// Selected theme
|
||||
pub theme: Theme,
|
||||
/// Path to the root of the unpacked files in /tmp
|
||||
temp_path: PathBuf,
|
||||
theme: Theme,
|
||||
/// Currently focused area
|
||||
focus: Focus,
|
||||
/// List of tabs
|
||||
tabs: Vec<Tab>,
|
||||
/// Current tab
|
||||
current_tab: ListState,
|
||||
/// Current search query
|
||||
|
@ -51,12 +51,13 @@ struct ListEntry {
|
|||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(theme: Theme, temp_path: PathBuf) -> Self {
|
||||
let root_id = TABS[0].tree.root().id();
|
||||
pub fn new(theme: Theme, temp_path: &Path, override_validation: bool) -> Self {
|
||||
let tabs = crate::tabs::get_tabs(temp_path, !override_validation);
|
||||
let root_id = tabs[0].tree.root().id();
|
||||
let mut state = Self {
|
||||
theme,
|
||||
temp_path,
|
||||
focus: Focus::List,
|
||||
tabs,
|
||||
current_tab: ListState::default().with_selected(Some(0)),
|
||||
search_query: String::new(),
|
||||
items: vec![],
|
||||
|
@ -67,7 +68,8 @@ impl AppState {
|
|||
state
|
||||
}
|
||||
pub fn draw(&mut self, frame: &mut Frame) {
|
||||
let longest_tab_display_len = TABS
|
||||
let longest_tab_display_len = self
|
||||
.tabs
|
||||
.iter()
|
||||
.map(|tab| tab.name.len() + self.theme.tab_icon().len())
|
||||
.max()
|
||||
|
@ -85,7 +87,11 @@ impl AppState {
|
|||
.constraints([Constraint::Length(3), Constraint::Min(1)])
|
||||
.split(horizontal[0]);
|
||||
|
||||
let tabs = TABS.iter().map(|tab| tab.name).collect::<Vec<_>>();
|
||||
let tabs = self
|
||||
.tabs
|
||||
.iter()
|
||||
.map(|tab| tab.name.as_str())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let tab_hl_style = if let Focus::TabList = self.focus {
|
||||
Style::default().reversed().fg(self.theme.tab_color())
|
||||
|
@ -186,7 +192,7 @@ impl AppState {
|
|||
self.focus = Focus::List
|
||||
}
|
||||
KeyCode::Char('j') | KeyCode::Down
|
||||
if self.current_tab.selected().unwrap() + 1 < TABS.len() =>
|
||||
if self.current_tab.selected().unwrap() + 1 < self.tabs.len() =>
|
||||
{
|
||||
self.current_tab.select_next();
|
||||
self.refresh_tab();
|
||||
|
@ -224,7 +230,7 @@ impl AppState {
|
|||
}
|
||||
pub fn update_items(&mut self) {
|
||||
if self.search_query.is_empty() {
|
||||
let curr = TABS[self.current_tab.selected().unwrap()]
|
||||
let curr = self.tabs[self.current_tab.selected().unwrap()]
|
||||
.tree
|
||||
.get(*self.visit_stack.last().unwrap())
|
||||
.unwrap();
|
||||
|
@ -241,7 +247,7 @@ impl AppState {
|
|||
self.items.clear();
|
||||
|
||||
let query_lower = self.search_query.to_lowercase();
|
||||
for tab in TABS.iter() {
|
||||
for tab in self.tabs.iter() {
|
||||
let mut stack = vec![tab.tree.root().id()];
|
||||
while let Some(node_id) = stack.pop() {
|
||||
let node = tab.tree.get(node_id).unwrap();
|
||||
|
@ -259,7 +265,7 @@ impl AppState {
|
|||
stack.extend(node.children().map(|child| child.id()));
|
||||
}
|
||||
}
|
||||
self.items.sort_by(|a, b| a.node.name.cmp(b.node.name));
|
||||
self.items.sort_by(|a, b| a.node.name.cmp(&b.node.name));
|
||||
}
|
||||
}
|
||||
/// Checks ehther the current tree node is the root node (can we go up the tree or no)
|
||||
|
@ -299,14 +305,14 @@ impl AppState {
|
|||
}
|
||||
fn enable_preview(&mut self) {
|
||||
if let Some(command) = self.get_selected_command(false) {
|
||||
if let Some(preview) = FloatingText::from_command(&command, self.temp_path.clone()) {
|
||||
if let Some(preview) = FloatingText::from_command(&command) {
|
||||
self.spawn_float(preview, 80, 80);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn handle_enter(&mut self) {
|
||||
if let Some(cmd) = self.get_selected_command(true) {
|
||||
let command = RunningCommand::new(cmd, &self.temp_path);
|
||||
let command = RunningCommand::new(cmd);
|
||||
self.spawn_float(command, 80, 80);
|
||||
}
|
||||
}
|
||||
|
@ -323,7 +329,10 @@ impl AppState {
|
|||
self.update_items();
|
||||
}
|
||||
fn refresh_tab(&mut self) {
|
||||
self.visit_stack = vec![TABS[self.current_tab.selected().unwrap()].tree.root().id()];
|
||||
self.visit_stack = vec![self.tabs[self.current_tab.selected().unwrap()]
|
||||
.tree
|
||||
.root()
|
||||
.id()];
|
||||
self.selection.select(Some(0));
|
||||
self.update_items();
|
||||
}
|
||||
|
|
341
src/tabs.rs
341
src/tabs.rs
|
@ -1,175 +1,190 @@
|
|||
use std::sync::LazyLock;
|
||||
|
||||
use ego_tree::{tree, Tree};
|
||||
|
||||
use crate::running_command::Command;
|
||||
use ego_tree::{NodeMut, Tree};
|
||||
use serde::Deserialize;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct TabList {
|
||||
directories: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct TabEntry {
|
||||
name: String,
|
||||
data: Vec<Entry>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Entry {
|
||||
name: String,
|
||||
#[allow(dead_code)]
|
||||
#[serde(default)]
|
||||
description: String,
|
||||
#[serde(default)]
|
||||
preconditions: Option<Vec<Precondition>>,
|
||||
#[serde(default)]
|
||||
entries: Option<Vec<Entry>>,
|
||||
#[serde(default)]
|
||||
command: Option<String>,
|
||||
#[serde(default)]
|
||||
script: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Entry {
|
||||
fn is_supported(&self) -> bool {
|
||||
self.preconditions.as_deref().map_or(true, |preconditions| {
|
||||
preconditions.iter().all(
|
||||
|Precondition {
|
||||
matches,
|
||||
data,
|
||||
values,
|
||||
}| {
|
||||
match data {
|
||||
SystemDataType::Environment(var_name) => std::env::var(var_name)
|
||||
.map_or(false, |var| values.contains(&var) == *matches),
|
||||
SystemDataType::File(path) => {
|
||||
std::fs::read_to_string(path).map_or(false, |data| {
|
||||
values
|
||||
.iter()
|
||||
.any(|matching_value| data.contains(matching_value))
|
||||
== *matches
|
||||
})
|
||||
}
|
||||
SystemDataType::CommandExists => values
|
||||
.iter()
|
||||
.all(|command| which::which(command).is_ok() == *matches),
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Precondition {
|
||||
// If true, the data must be contained within the list of values.
|
||||
// Otherwise, the data must not be contained within the list of values
|
||||
matches: bool,
|
||||
data: SystemDataType,
|
||||
values: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
enum SystemDataType {
|
||||
#[serde(rename = "environment")]
|
||||
Environment(String),
|
||||
#[serde(rename = "file")]
|
||||
File(PathBuf),
|
||||
#[serde(rename = "command_exists")]
|
||||
CommandExists,
|
||||
}
|
||||
|
||||
#[derive(Hash, Eq, PartialEq)]
|
||||
pub struct Tab {
|
||||
pub name: &'static str,
|
||||
pub name: String,
|
||||
pub tree: Tree<ListNode>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
pub struct ListNode {
|
||||
pub name: &'static str,
|
||||
pub name: String,
|
||||
pub command: Command,
|
||||
}
|
||||
|
||||
pub static TABS: LazyLock<Vec<Tab>> = LazyLock::new(|| {
|
||||
vec![
|
||||
Tab {
|
||||
name: "System Setup",
|
||||
tree: tree!(ListNode {
|
||||
name: "root",
|
||||
command: Command::None,
|
||||
} => {
|
||||
ListNode {
|
||||
name: "Arch Linux",
|
||||
command: Command::None,
|
||||
} => {
|
||||
ListNode {
|
||||
name: "Yay AUR Helper",
|
||||
command: Command::LocalFile("system-setup/arch/yay-setup.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Paru AUR Helper",
|
||||
command: Command::LocalFile("system-setup/arch/paru-setup.sh"),
|
||||
pub fn get_tabs(command_dir: &Path, validate: bool) -> Vec<Tab> {
|
||||
let tab_files = TabList::get_tabs(command_dir);
|
||||
let tabs = tab_files.into_iter().map(|path| {
|
||||
let directory = path.parent().unwrap().to_owned();
|
||||
let data = std::fs::read_to_string(path).expect("Failed to read tab data");
|
||||
let mut tab_data: TabEntry = toml::from_str(&data).expect("Failed to parse tab data");
|
||||
|
||||
if validate {
|
||||
filter_entries(&mut tab_data.data);
|
||||
}
|
||||
},
|
||||
ListNode {
|
||||
name: "Full System Update",
|
||||
command: Command::LocalFile("system-update.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Build Prerequisites",
|
||||
command: Command::LocalFile("system-setup/1-compile-setup.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Gaming Dependencies",
|
||||
command: Command::LocalFile("system-setup/2-gaming-setup.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Global Theme",
|
||||
command: Command::LocalFile("system-setup/3-global-theme.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Remove Snaps",
|
||||
command: Command::LocalFile("system-setup/4-remove-snaps.sh"),
|
||||
}
|
||||
}),
|
||||
},
|
||||
Tab {
|
||||
name: "Applications Setup",
|
||||
tree: tree!(ListNode {
|
||||
name: "root",
|
||||
command: Command::None,
|
||||
} => {
|
||||
ListNode {
|
||||
name: "Alacritty",
|
||||
command: Command::LocalFile("applications-setup/alacritty-setup.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Bash Prompt",
|
||||
command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/mybash/main/setup.sh)\""),
|
||||
},
|
||||
ListNode {
|
||||
name: "DWM-Titus",
|
||||
command: Command::LocalFile("applications-setup/dwmtitus-setup.sh")
|
||||
},
|
||||
ListNode {
|
||||
name: "Kitty",
|
||||
command: Command::LocalFile("applications-setup/kitty-setup.sh")
|
||||
},
|
||||
ListNode {
|
||||
name: "Neovim",
|
||||
command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/neovim/main/setup.sh)\""),
|
||||
},
|
||||
ListNode {
|
||||
name: "Rofi",
|
||||
command: Command::LocalFile("applications-setup/rofi-setup.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "ZSH Prompt",
|
||||
command: Command::LocalFile("applications-setup/zsh-setup.sh"),
|
||||
}
|
||||
}),
|
||||
},
|
||||
Tab {
|
||||
name: "Security",
|
||||
tree: tree!(ListNode {
|
||||
name: "root",
|
||||
command: Command::None,
|
||||
} => {
|
||||
ListNode {
|
||||
name: "Firewall Baselines (CTT)",
|
||||
command: Command::LocalFile("security/firewall-baselines.sh"),
|
||||
}
|
||||
}),
|
||||
},
|
||||
Tab {
|
||||
name: "Utilities",
|
||||
tree: tree!(ListNode {
|
||||
name: "root",
|
||||
command: Command::None,
|
||||
} => {
|
||||
ListNode {
|
||||
name: "Wifi Manager",
|
||||
command: Command::LocalFile("utils/wifi-control.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Bluetooth Manager",
|
||||
command: Command::LocalFile("utils/bluetooth-control.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "MonitorControl(xorg)",
|
||||
command: Command::None,
|
||||
} => {
|
||||
ListNode {
|
||||
name: "Set Resolution",
|
||||
command: Command::LocalFile("utils/monitor-control/set_resolutions.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Duplicate Displays",
|
||||
command: Command::LocalFile("utils/monitor-control/duplicate_displays.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Extend Displays",
|
||||
command: Command::LocalFile("utils/monitor-control/extend_displays.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Auto Detect Displays",
|
||||
command: Command::LocalFile("utils/monitor-control/auto_detect_displays.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Enable Monitor",
|
||||
command: Command::LocalFile("utils/monitor-control/enable_monitor.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Disable Monitor",
|
||||
command: Command::LocalFile("utils/monitor-control/disable_monitor.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Set Primary Monitor",
|
||||
command: Command::LocalFile("utils/monitor-control/set_primary_monitor.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Change Orientation",
|
||||
command: Command::LocalFile("utils/monitor-control/change_orientation.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Manage Arrangement",
|
||||
command: Command::LocalFile("utils/monitor-control/manage_arrangement.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Scale Monitors",
|
||||
command: Command::LocalFile("utils/monitor-control/scale_monitor.sh"),
|
||||
},
|
||||
ListNode {
|
||||
name: "Reset Scaling",
|
||||
command: Command::LocalFile("utils/monitor-control/reset_scaling.sh"),
|
||||
}
|
||||
},
|
||||
}),
|
||||
},
|
||||
]
|
||||
(tab_data, directory)
|
||||
});
|
||||
|
||||
let tabs: Vec<Tab> = tabs
|
||||
.map(|(TabEntry { name, data }, directory)| {
|
||||
let mut tree = Tree::new(ListNode {
|
||||
name: "root".to_string(),
|
||||
command: Command::None,
|
||||
});
|
||||
let mut root = tree.root_mut();
|
||||
create_directory(data, &mut root, &directory);
|
||||
Tab { name, tree }
|
||||
})
|
||||
.collect();
|
||||
|
||||
if tabs.is_empty() {
|
||||
panic!("No tabs found");
|
||||
}
|
||||
tabs
|
||||
}
|
||||
|
||||
fn filter_entries(entries: &mut Vec<Entry>) {
|
||||
entries.retain_mut(|entry| {
|
||||
if !entry.is_supported() {
|
||||
return false;
|
||||
}
|
||||
if let Some(entries) = &mut entry.entries {
|
||||
filter_entries(entries);
|
||||
!entries.is_empty()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn create_directory(data: Vec<Entry>, node: &mut NodeMut<ListNode>, command_dir: &Path) {
|
||||
for entry in data {
|
||||
if [
|
||||
entry.entries.is_some(),
|
||||
entry.command.is_some(),
|
||||
entry.script.is_some(),
|
||||
]
|
||||
.iter()
|
||||
.filter(|&&x| x)
|
||||
.count()
|
||||
> 1
|
||||
{
|
||||
panic!("Entry must have only one data type");
|
||||
}
|
||||
|
||||
if let Some(entries) = entry.entries {
|
||||
let mut node = node.append(ListNode {
|
||||
name: entry.name,
|
||||
command: Command::None,
|
||||
});
|
||||
create_directory(entries, &mut node, command_dir);
|
||||
} else if let Some(command) = entry.command {
|
||||
node.append(ListNode {
|
||||
name: entry.name,
|
||||
command: Command::Raw(command),
|
||||
});
|
||||
} else if let Some(script) = entry.script {
|
||||
let dir = command_dir.join(script);
|
||||
if !dir.exists() {
|
||||
panic!("Script {} does not exist", dir.display());
|
||||
}
|
||||
node.append(ListNode {
|
||||
name: entry.name,
|
||||
command: Command::LocalFile(dir),
|
||||
});
|
||||
} else {
|
||||
panic!("Entry must have data");
|
||||
}
|
||||
}
|
||||
}
|
||||
impl TabList {
|
||||
fn get_tabs(command_dir: &Path) -> Vec<PathBuf> {
|
||||
let tab_files = std::fs::read_to_string(command_dir.join("tabs.toml"))
|
||||
.expect("Failed to read tabs.toml");
|
||||
let data: Self = toml::from_str(&tab_files).expect("Failed to parse tabs.toml");
|
||||
|
||||
data.directories
|
||||
.into_iter()
|
||||
.map(|path| command_dir.join(path).join("tab_data.toml"))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user