mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-22 13:22:28 +00:00
Merge 4f5030c359
into 8ea3da9843
This commit is contained in:
commit
42deb2e5b2
22
README.md
22
README.md
|
@ -72,6 +72,28 @@ Note that crates installed using `cargo install` require manual updating with `c
|
|||
|
||||
</details>
|
||||
|
||||
## Configuration
|
||||
|
||||
Linutil supports configuration through a TOML config file. Path to the file can be specified with `--config` (or `-c`).
|
||||
|
||||
Available options:
|
||||
- `auto_execute` - a list of commands to execute automatically (can be combined with `--skip-confirmation`)
|
||||
|
||||
Example config:
|
||||
```toml
|
||||
# example_config.toml
|
||||
|
||||
auto_execute = [
|
||||
"Fastfetch",
|
||||
"Alacritty",
|
||||
"Kitty"
|
||||
]
|
||||
```
|
||||
|
||||
```bash
|
||||
linutil --config /path/to/example_config.toml
|
||||
```
|
||||
|
||||
## 💖 Support
|
||||
|
||||
If you find Linutil helpful, please consider giving it a ⭐️ to show your support!
|
||||
|
|
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;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
@ -5,6 +6,7 @@ use std::rc::Rc;
|
|||
use ego_tree::Tree;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub use config::Config;
|
||||
pub use inner::{get_tabs, TabList};
|
||||
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
|
@ -33,3 +35,16 @@ pub struct ListNode {
|
|||
pub task_list: String,
|
||||
pub multi_select: bool,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
## Milestones
|
||||
### Q4 2024
|
||||
- [x] Finish the foundation of the project's CLI
|
||||
- [ ] Implement CLI arguments and configuration support
|
||||
- [x] Implement CLI arguments and configuration support
|
||||
- [ ] Add an option for logging script executions
|
||||
|
||||
### Q1 2025
|
||||
|
|
|
@ -22,6 +22,10 @@ curl -fsSL https://christitus.com/linux | sh
|
|||
curl -fsSL https://christitus.com/linuxdev | sh
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-config\fR \fI<path>\fR
|
||||
Path to the configuration file.
|
||||
|
||||
.TP
|
||||
\fB\-t\fR, \fB\-\-theme\fR \fI<theme>\fR
|
||||
Set the theme to use in the TUI.
|
||||
|
|
|
@ -9,6 +9,7 @@ mod theme;
|
|||
|
||||
use std::{
|
||||
io::{self, stdout},
|
||||
path::PathBuf,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
|
@ -30,6 +31,8 @@ use state::AppState;
|
|||
// Linux utility toolbox
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
#[arg(short, long, help = "Path to the configuration file")]
|
||||
config: Option<PathBuf>,
|
||||
#[arg(short, long, value_enum)]
|
||||
#[arg(default_value_t = Theme::Default)]
|
||||
#[arg(help = "Set the theme to use in the application")]
|
||||
|
@ -45,7 +48,12 @@ struct Args {
|
|||
fn main() -> io::Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
let mut state = AppState::new(args.theme, args.override_validation, args.size_bypass);
|
||||
let mut state = AppState::new(
|
||||
args.config,
|
||||
args.theme,
|
||||
args.override_validation,
|
||||
args.size_bypass,
|
||||
);
|
||||
|
||||
stdout().execute(EnterAlternateScreen)?;
|
||||
enable_raw_mode()?;
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
theme::Theme,
|
||||
};
|
||||
use ego_tree::NodeId;
|
||||
use linutil_core::{ListNode, TabList};
|
||||
use linutil_core::{Config, ListNode, TabList};
|
||||
#[cfg(feature = "tips")]
|
||||
use rand::Rng;
|
||||
use ratatui::{
|
||||
|
@ -19,6 +19,7 @@ use ratatui::{
|
|||
widgets::{Block, Borders, List, ListState, Paragraph},
|
||||
Frame,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
|
||||
const MIN_WIDTH: u16 = 100;
|
||||
|
@ -85,10 +86,17 @@ enum SelectedItem {
|
|||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(theme: Theme, override_validation: bool, size_bypass: bool) -> Self {
|
||||
pub fn new(
|
||||
config_path: Option<PathBuf>,
|
||||
theme: Theme,
|
||||
override_validation: bool,
|
||||
size_bypass: bool,
|
||||
) -> Self {
|
||||
let tabs = linutil_core::get_tabs(!override_validation);
|
||||
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 {
|
||||
theme,
|
||||
focus: Focus::List,
|
||||
|
@ -106,9 +114,31 @@ impl AppState {
|
|||
};
|
||||
|
||||
state.update_items();
|
||||
if let Some(auto_execute_commands) = auto_execute_commands {
|
||||
state.handle_initial_auto_execute(&auto_execute_commands);
|
||||
}
|
||||
|
||||
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]> {
|
||||
if self.selected_item_is_dir() {
|
||||
Box::new([Shortcut::new("Go to selected dir", ["l", "Right", "Enter"])])
|
||||
|
|
Loading…
Reference in New Issue
Block a user