mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-25 14:30:11 +00:00
Compare commits
3 Commits
c34a44abae
...
2e10ad471e
Author | SHA1 | Date | |
---|---|---|---|
|
2e10ad471e | ||
|
696110eae5 | ||
|
4dbcca736d |
19
.github/release.yml
vendored
19
.github/release.yml
vendored
|
@ -1,20 +1,23 @@
|
||||||
changelog:
|
changelog:
|
||||||
categories:
|
categories:
|
||||||
- title: '🚀 Features'
|
- title: '🚀 Features'
|
||||||
labels:
|
label: 'enhancement'
|
||||||
- 'feature'
|
|
||||||
- 'enhancement'
|
|
||||||
- title: '🐛 Bug Fixes'
|
- title: '🐛 Bug Fixes'
|
||||||
labels:
|
label: 'bug'
|
||||||
- 'fix'
|
- title: '⚙️ Refactoring'
|
||||||
- 'bugfix'
|
label: 'refactor'
|
||||||
- 'bug'
|
- title: '🧩 UI/UX'
|
||||||
|
label: 'UI/UX'
|
||||||
- title: '📚 Documentation'
|
- title: '📚 Documentation'
|
||||||
label: 'documentation'
|
label: 'documentation'
|
||||||
- title: '🔒 Security'
|
- title: '🔒 Security'
|
||||||
label: 'security'
|
label: 'security'
|
||||||
- title: '🧰 GitHub Actions'
|
- title: '🧰 GitHub Actions'
|
||||||
label: 'github actions'
|
label: 'github_actions'
|
||||||
|
- title: '🦀 Rust'
|
||||||
|
label: 'rust'
|
||||||
|
- title: '📃 Scripting'
|
||||||
|
label: 'script'
|
||||||
exclude:
|
exclude:
|
||||||
labels:
|
labels:
|
||||||
- 'skip-changelog'
|
- 'skip-changelog'
|
28
core/src/config.rs
Normal file
28
core/src/config.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub auto_execute: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn from_file(path: &Path) -> Self {
|
||||||
|
let content = match std::fs::read_to_string(path) {
|
||||||
|
Ok(content) => content,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to read config file {}: {}", path.display(), e);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match toml::from_str(&content) {
|
||||||
|
Ok(config) => config,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to parse config file: {}", e);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod config;
|
||||||
mod inner;
|
mod inner;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -5,6 +6,7 @@ use std::rc::Rc;
|
||||||
use ego_tree::Tree;
|
use ego_tree::Tree;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub use config::Config;
|
||||||
pub use inner::get_tabs;
|
pub use inner::get_tabs;
|
||||||
|
|
||||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||||
|
@ -33,3 +35,16 @@ pub struct ListNode {
|
||||||
pub command: Command,
|
pub command: Command,
|
||||||
pub task_list: String,
|
pub task_list: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Tab {
|
||||||
|
pub fn find_command(&self, name: &str) -> Option<Rc<ListNode>> {
|
||||||
|
self.tree.root().descendants().find_map(|node| {
|
||||||
|
let value = node.value();
|
||||||
|
if value.name == name && !node.has_children() {
|
||||||
|
Some(value.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ mod theme;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
io::{self, stdout},
|
io::{self, stdout},
|
||||||
|
path::PathBuf,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,6 +27,11 @@ use state::AppState;
|
||||||
// Linux utility toolbox
|
// Linux utility toolbox
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
struct Args {
|
struct Args {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Path to the configuration file for automatic command selection"
|
||||||
|
)]
|
||||||
|
config: Option<PathBuf>,
|
||||||
#[arg(short, long, value_enum)]
|
#[arg(short, long, value_enum)]
|
||||||
#[arg(default_value_t = Theme::Default)]
|
#[arg(default_value_t = Theme::Default)]
|
||||||
#[arg(help = "Set the theme to use in the application")]
|
#[arg(help = "Set the theme to use in the application")]
|
||||||
|
@ -38,7 +44,7 @@ struct Args {
|
||||||
fn main() -> io::Result<()> {
|
fn main() -> io::Result<()> {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
let mut state = AppState::new(args.theme, args.override_validation);
|
let mut state = AppState::new(args.theme, args.override_validation, args.config);
|
||||||
|
|
||||||
stdout().execute(EnterAlternateScreen)?;
|
stdout().execute(EnterAlternateScreen)?;
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
|
|
|
@ -9,6 +9,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
|
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
|
||||||
use ego_tree::NodeId;
|
use ego_tree::NodeId;
|
||||||
|
use linutil_core::Config;
|
||||||
use linutil_core::{ListNode, Tab};
|
use linutil_core::{ListNode, Tab};
|
||||||
#[cfg(feature = "tips")]
|
#[cfg(feature = "tips")]
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
@ -19,6 +20,7 @@ use ratatui::{
|
||||||
widgets::{Block, Borders, List, ListState, Paragraph},
|
widgets::{Block, Borders, List, ListState, Paragraph},
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use temp_dir::TempDir;
|
use temp_dir::TempDir;
|
||||||
|
|
||||||
|
@ -79,10 +81,12 @@ pub struct ListEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
pub fn new(theme: Theme, override_validation: bool) -> Self {
|
pub fn new(theme: Theme, override_validation: bool, config_path: Option<PathBuf>) -> Self {
|
||||||
let (temp_dir, tabs) = linutil_core::get_tabs(!override_validation);
|
let (temp_dir, tabs) = linutil_core::get_tabs(!override_validation);
|
||||||
let root_id = tabs[0].tree.root().id();
|
let root_id = tabs[0].tree.root().id();
|
||||||
|
|
||||||
|
let auto_execute_commands = config_path.map(|path| Config::from_file(&path).auto_execute);
|
||||||
|
|
||||||
let mut state = Self {
|
let mut state = Self {
|
||||||
_temp_dir: temp_dir,
|
_temp_dir: temp_dir,
|
||||||
theme,
|
theme,
|
||||||
|
@ -100,9 +104,31 @@ impl AppState {
|
||||||
};
|
};
|
||||||
|
|
||||||
state.update_items();
|
state.update_items();
|
||||||
|
if let Some(auto_execute_commands) = auto_execute_commands {
|
||||||
|
state.handle_initial_auto_execute(&auto_execute_commands);
|
||||||
|
}
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_initial_auto_execute(&mut self, auto_execute_commands: &[String]) {
|
||||||
|
self.selected_commands = auto_execute_commands
|
||||||
|
.iter()
|
||||||
|
.filter_map(|name| self.tabs.iter().find_map(|tab| tab.find_command(name)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if !self.selected_commands.is_empty() {
|
||||||
|
let cmd_names: Vec<_> = self
|
||||||
|
.selected_commands
|
||||||
|
.iter()
|
||||||
|
.map(|node| node.name.as_str())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let prompt = ConfirmPrompt::new(&cmd_names);
|
||||||
|
self.focus = Focus::ConfirmationPrompt(Float::new(Box::new(prompt), 40, 40));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_list_item_shortcut(&self) -> Box<[Shortcut]> {
|
fn get_list_item_shortcut(&self) -> Box<[Shortcut]> {
|
||||||
if self.selected_item_is_dir() {
|
if self.selected_item_is_dir() {
|
||||||
Box::new([Shortcut::new("Go to selected dir", ["l", "Right", "Enter"])])
|
Box::new([Shortcut::new("Go to selected dir", ["l", "Right", "Enter"])])
|
||||||
|
|
Loading…
Reference in New Issue
Block a user