refactor: Split linutil into TUI and Core crates

This commit is contained in:
Liam 2024-09-05 16:39:10 -07:00
parent 412806a853
commit 87869593a5
No known key found for this signature in database
55 changed files with 323 additions and 131 deletions

208
Cargo.lock generated
View File

@ -20,6 +20,21 @@ version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anstream"
version = "0.6.14"
@ -99,6 +114,12 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "cassowary"
version = "0.3.0"
@ -114,12 +135,35 @@ dependencies = [
"rustversion",
]
[[package]]
name = "cc"
version = "1.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets 0.52.5",
]
[[package]]
name = "clap"
version = "4.5.16"
@ -179,6 +223,12 @@ dependencies = [
"static_assertions",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "crossterm"
version = "0.27.0"
@ -280,6 +330,29 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "iana-time-zone"
version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "include_dir"
version = "0.7.4"
@ -339,6 +412,15 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "js-sys"
version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -351,6 +433,34 @@ version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "linutil_core"
version = "0.1.0"
dependencies = [
"ego-tree",
"include_dir",
"serde",
"tempdir",
"toml",
"which",
]
[[package]]
name = "linutil_tui"
version = "0.1.0"
dependencies = [
"chrono",
"clap",
"crossterm",
"ego-tree",
"linutil_core",
"oneshot",
"portable-pty",
"ratatui",
"tui-term",
"unicode-width",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
@ -423,6 +533,15 @@ dependencies = [
"pin-utils",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.19.0"
@ -703,6 +822,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook"
version = "0.3.17"
@ -867,25 +992,6 @@ dependencies = [
"winnow",
]
[[package]]
name = "tui"
version = "0.1.0"
dependencies = [
"clap",
"crossterm",
"ego-tree",
"include_dir",
"oneshot",
"portable-pty",
"ratatui",
"serde",
"tempdir",
"toml",
"tui-term",
"unicode-width",
"which",
]
[[package]]
name = "tui-term"
version = "0.1.12"
@ -976,6 +1082,61 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
dependencies = [
"cfg-if",
"once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
[[package]]
name = "which"
version = "6.0.3"
@ -1010,6 +1171,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.5",
]
[[package]]
name = "windows-sys"
version = "0.48.0"

View File

@ -1,34 +1,17 @@
[package]
name = "tui"
[workspace.package]
license = "MIT"
version = "0.1.0"
edition = "2021"
[dependencies]
clap = { version = "4.5.16", features = ["derive"] }
crossterm = "0.27.0"
# Use workspace dependencies for anything public in linutil_core to avoid versioning conflicts
[workspace.dependencies]
ego-tree = "0.6.2"
oneshot = "0.1.8"
portable-pty = "0.8.1"
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.3"
unicode-width = "0.1.13"
[build-dependencies]
chrono = "0.4.33"
[[bin]]
name = "linutil"
path = "src/main.rs"
[workspace]
members = ["tui", "core"]
resolver = "2"
[profile.release]
opt-level = 3
opt-level = "z"
debug = false
lto = true
codegen-units = 1

View File

@ -1,12 +0,0 @@
fn main() {
// Rebuild program if any file in commands directory changes.
println!("cargo:rerun-if-changed=src/commands");
// Rerun build script if any code is modified
println!("cargo:rerun-if-changed=src");
// Add current date as a variable to be displayed in the 'Linux Toolbox' text.
println!(
"cargo:rustc-env=BUILD_DATE={}",
chrono::Local::now().format("%Y-%m-%d")
);
}

13
core/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "linutil_core"
edition = "2021"
version.workspace = true
license.workspace = true
[dependencies]
include_dir = "0.7.4"
tempdir = "0.3.7"
serde = { version = "1.0.205", features = ["derive"] }
toml = "0.8.19"
which = "6.0.3"
ego-tree = { workspace = true }

View File

@ -1,7 +1,42 @@
use crate::running_command::Command;
use crate::{Command, ListNode, Tab};
use ego_tree::{NodeMut, Tree};
use include_dir::{include_dir, Dir};
use serde::Deserialize;
use std::path::{Path, PathBuf};
use tempdir::TempDir;
const TAB_DATA: Dir = include_dir!("$CARGO_MANIFEST_DIR/../tabs");
pub fn get_tabs(validate: bool) -> Vec<Tab> {
let tab_files = TabList::get_tabs();
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);
}
(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
}
#[derive(Deserialize)]
struct TabList {
@ -79,49 +114,6 @@ enum SystemDataType {
CommandExists,
}
#[derive(Hash, Eq, PartialEq)]
pub struct Tab {
pub name: String,
pub tree: Tree<ListNode>,
}
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct ListNode {
pub name: String,
pub command: Command,
}
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);
}
(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() {
@ -176,15 +168,21 @@ fn create_directory(data: Vec<Entry>, node: &mut NodeMut<ListNode>, command_dir:
}
}
}
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");
fn get_tabs() -> Vec<PathBuf> {
let temp_dir = TempDir::new("linutil_scripts").unwrap().into_path();
TAB_DATA
.extract(&temp_dir)
.expect("Failed to extract the saved directory");
let tab_files =
std::fs::read_to_string(temp_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"))
.iter()
.map(|path| temp_dir.join(path).join("tab_data.toml"))
.collect()
}
}

25
core/src/lib.rs Normal file
View File

@ -0,0 +1,25 @@
mod inner;
use ego_tree::Tree;
use std::path::PathBuf;
pub use inner::get_tabs;
#[derive(Clone, Hash, Eq, PartialEq)]
pub enum Command {
Raw(String),
LocalFile(PathBuf),
None, // Directory
}
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct Tab {
pub name: String,
pub tree: Tree<ListNode>,
}
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct ListNode {
pub name: String,
pub command: Command,
}

23
tui/Cargo.toml Normal file
View File

@ -0,0 +1,23 @@
[package]
name = "linutil_tui"
edition = "2021"
version.workspace = true
license.workspace = true
[dependencies]
clap = { version = "4.5.16", features = ["derive"] }
crossterm = "0.27.0"
ego-tree = { workspace = true }
oneshot = "0.1.8"
portable-pty = "0.8.1"
ratatui = "0.27.0"
tui-term = "0.1.12"
unicode-width = "0.1.13"
linutil_core = { path = "../core" }
[build-dependencies]
chrono = "0.4.33"
[[bin]]
name = "linutil"
path = "src/main.rs"

7
tui/build.rs Normal file
View File

@ -0,0 +1,7 @@
fn main() {
// Add current date as a variable to be displayed in the 'Linux Toolbox' text.
println!(
"cargo:rustc-env=BUILD_DATE={}",
chrono::Local::now().format("%Y-%m-%d")
);
}

View File

@ -1,6 +1,7 @@
use crate::{state::ListEntry, tabs::Tab, theme::Theme};
use crate::{state::ListEntry, theme::Theme};
use crossterm::event::{KeyCode, KeyEvent};
use ego_tree::NodeId;
use linutil_core::Tab;
use ratatui::{
layout::Rect,
style::Style,

View File

@ -1,5 +1,6 @@
use crate::{float::FloatContent, running_command::Command};
use crate::float::FloatContent;
use crossterm::event::{KeyCode, KeyEvent};
use linutil_core::Command;
use ratatui::{
layout::Rect,
style::{Style, Stylize},

View File

@ -3,7 +3,6 @@ mod float;
mod floating_text;
mod running_command;
pub mod state;
mod tabs;
mod theme;
use std::{
@ -20,13 +19,11 @@ use crossterm::{
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
ExecutableCommand,
};
use include_dir::include_dir;
use ratatui::{
backend::{Backend, CrosstermBackend},
Terminal,
};
use state::AppState;
use tempdir::TempDir;
// Linux utility toolbox
#[derive(Debug, Parser)]
@ -43,13 +40,7 @@ struct Args {
fn main() -> std::io::Result<()> {
let args = Args::parse();
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(args.theme, temp_dir.path(), args.override_validation);
let mut state = AppState::new(args.theme, args.override_validation);
stdout().execute(EnterAlternateScreen)?;
enable_raw_mode()?;

View File

@ -1,5 +1,6 @@
use crate::float::FloatContent;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use linutil_core::Command;
use oneshot::{channel, Receiver};
use portable_pty::{
ChildKiller, CommandBuilder, ExitStatus, MasterPty, NativePtySystem, PtySize, PtySystem,
@ -13,7 +14,6 @@ use ratatui::{
};
use std::{
io::Write,
path::PathBuf,
sync::{Arc, Mutex},
thread::JoinHandle,
};
@ -22,13 +22,6 @@ use tui_term::{
widget::PseudoTerminal,
};
#[derive(Clone, Hash, Eq, PartialEq)]
pub enum Command {
Raw(String),
LocalFile(PathBuf),
None, // Directory
}
pub struct RunningCommand {
/// A buffer to save all the command output (accumulates, until the command exits)
buffer: Arc<Mutex<Vec<u8>>>,

View File

@ -2,12 +2,12 @@ use crate::{
filter::{Filter, SearchAction},
float::{Float, FloatContent},
floating_text::FloatingText,
running_command::{Command, RunningCommand},
tabs::{ListNode, Tab},
running_command::RunningCommand,
theme::Theme,
};
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ego_tree::NodeId;
use linutil_core::{Command, ListNode, Tab};
use ratatui::{
layout::{Constraint, Direction, Layout},
style::{Style, Stylize},
@ -15,7 +15,6 @@ use ratatui::{
widgets::{Block, Borders, List, ListState},
Frame,
};
use std::path::Path;
pub struct AppState {
/// Selected theme
@ -49,8 +48,8 @@ pub struct ListEntry {
}
impl AppState {
pub fn new(theme: Theme, temp_path: &Path, override_validation: bool) -> Self {
let tabs = crate::tabs::get_tabs(temp_path, !override_validation);
pub fn new(theme: Theme, override_validation: bool) -> Self {
let tabs = linutil_core::get_tabs(!override_validation);
let root_id = tabs[0].tree.root().id();
let mut state = Self {
theme,