mirror of
https://codeberg.org/iff/pay-respects.git
synced 2025-10-06 00:22:43 +02:00
feat: config file
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -912,7 +912,9 @@ dependencies = [
|
||||
"pay-respects-utils",
|
||||
"regex-lite",
|
||||
"rust-i18n",
|
||||
"serde",
|
||||
"sys-locale",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
24
config.md
Normal file
24
config.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Configuration File
|
||||
|
||||
Configuration file for `pay-respects` is located at:
|
||||
|
||||
- `$HOME/.config/pay-respects/config.toml` (*nix)
|
||||
- `%APPDATA%/pay-respects/config.toml` (Windows)
|
||||
|
||||
## Options
|
||||
|
||||
All available options are listed in the following example file:
|
||||
```toml
|
||||
# maximum time in milliseconds for getting previous output
|
||||
timeout = 3000
|
||||
# your preferred command for privileges
|
||||
sudo = "run0"
|
||||
|
||||
[package_manager]
|
||||
# preferred package manager
|
||||
package_manager = "pacman"
|
||||
|
||||
# preferred installation method, can be limited with the package manager
|
||||
# available options are: System, User, Temp
|
||||
install_method = "System"
|
||||
```
|
@@ -22,6 +22,10 @@ askama = "0.13"
|
||||
|
||||
inquire = "0.7"
|
||||
|
||||
# config file
|
||||
toml = { version = "0.8" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
pay-respects-parser = { version = "0.3", path = "../parser" }
|
||||
pay-respects-utils = { version ="0.1", path = "../utils"}
|
||||
itertools = "0.14.0"
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use crate::shell::{initialization, Init};
|
||||
use crate::{init::Init, shell::initialization};
|
||||
use colored::Colorize;
|
||||
|
||||
pub enum Status {
|
||||
|
67
core/src/config.rs
Normal file
67
core/src/config.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Default)]
|
||||
pub struct Config {
|
||||
pub sudo: Option<String>,
|
||||
#[serde(default)]
|
||||
pub timeout: Timeout,
|
||||
#[serde(default)]
|
||||
pub package_manager: PackageManagerConfig,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Default)]
|
||||
pub struct PackageManagerConfig {
|
||||
pub package_manager: Option<String>,
|
||||
#[serde(default)]
|
||||
pub install_method: InstallMethod,
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Timeout(pub u64);
|
||||
impl Default for Timeout {
|
||||
fn default() -> Self {
|
||||
Timeout(3000)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Default)]
|
||||
pub enum InstallMethod {
|
||||
#[default]
|
||||
System,
|
||||
User,
|
||||
Temp,
|
||||
}
|
||||
|
||||
pub fn load_config() -> Config {
|
||||
let path = config_path();
|
||||
let exists = std::path::Path::new(&path).exists();
|
||||
if exists {
|
||||
let content = std::fs::read_to_string(&path).expect("Failed to read config file");
|
||||
let config: Config = toml::from_str(&content).unwrap_or_else(|_| {
|
||||
eprintln!(
|
||||
"Failed to parse config file at {}. Using default configuration.",
|
||||
path
|
||||
);
|
||||
Config::default()
|
||||
});
|
||||
return config;
|
||||
}
|
||||
Config::default()
|
||||
}
|
||||
|
||||
fn config_path() -> String {
|
||||
#[cfg(windows)]
|
||||
let xdg_config_home = std::env::var("APPDATA").unwrap();
|
||||
#[cfg(not(windows))]
|
||||
let xdg_config_home = std::env::var("XDG_CONFIG_HOME")
|
||||
.unwrap_or_else(|_| std::env::var("HOME").unwrap() + "/.config");
|
||||
|
||||
format!("{}/pay-respects/config.toml", xdg_config_home)
|
||||
}
|
288
core/src/data.rs
Normal file
288
core/src/data.rs
Normal file
@@ -0,0 +1,288 @@
|
||||
use pay_respects_utils::evals::split_command;
|
||||
use pay_respects_utils::files::get_path_files;
|
||||
use pay_respects_utils::files::path_env_sep;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use std::process::exit;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[cfg(windows)]
|
||||
use pay_respects_utils::files::path_convert;
|
||||
|
||||
use crate::config::load_config;
|
||||
use crate::config::Config;
|
||||
use crate::shell::alias_map;
|
||||
use crate::shell::builtin_commands;
|
||||
use crate::shell::expand_alias_multiline;
|
||||
use crate::shell::get_error;
|
||||
use crate::shell::get_shell;
|
||||
use crate::shell::last_command;
|
||||
use crate::shell::run_mode;
|
||||
|
||||
pub const PRIVILEGE_LIST: [&str; 2] = ["sudo", "doas"];
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum Mode {
|
||||
Suggestion,
|
||||
Echo,
|
||||
NoConfirm,
|
||||
Cnf,
|
||||
}
|
||||
pub struct Data {
|
||||
pub shell: String,
|
||||
pub env: Option<String>,
|
||||
pub command: String,
|
||||
pub suggest: Option<String>,
|
||||
pub candidates: Vec<String>,
|
||||
pub split: Vec<String>,
|
||||
pub alias: Option<HashMap<String, String>>,
|
||||
pub privilege: Option<String>,
|
||||
pub error: String,
|
||||
pub executables: Vec<String>,
|
||||
pub modules: Vec<String>,
|
||||
pub fallbacks: Vec<String>,
|
||||
pub config: Config,
|
||||
pub mode: Mode,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub fn init() -> Data {
|
||||
let shell = get_shell();
|
||||
let command = last_command(&shell).trim().to_string();
|
||||
let alias = alias_map(&shell);
|
||||
let mode = run_mode();
|
||||
let (mut executables, modules, fallbacks);
|
||||
let lib_dir = {
|
||||
if let Ok(lib_dir) = std::env::var("_PR_LIB") {
|
||||
Some(lib_dir)
|
||||
} else {
|
||||
option_env!("_DEF_PR_LIB").map(|dir| dir.to_string())
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("lib_dir: {:?}", lib_dir);
|
||||
|
||||
if lib_dir.is_none() {
|
||||
(executables, modules, fallbacks) = {
|
||||
let path_executables = get_path_files();
|
||||
let mut executables = vec![];
|
||||
let mut modules = vec![];
|
||||
let mut fallbacks = vec![];
|
||||
for exe in path_executables {
|
||||
if exe.starts_with("_pay-respects-module-") {
|
||||
modules.push(exe.to_string());
|
||||
} else if exe.starts_with("_pay-respects-fallback-") {
|
||||
fallbacks.push(exe.to_string());
|
||||
} else {
|
||||
executables.push(exe.to_string());
|
||||
}
|
||||
}
|
||||
modules.sort_unstable();
|
||||
fallbacks.sort_unstable();
|
||||
if alias.is_some() {
|
||||
let alias = alias.as_ref().unwrap();
|
||||
for command in alias.keys() {
|
||||
if executables.contains(command) {
|
||||
continue;
|
||||
}
|
||||
executables.push(command.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
(executables, modules, fallbacks)
|
||||
};
|
||||
} else {
|
||||
(executables, modules, fallbacks) = {
|
||||
let mut modules = vec![];
|
||||
let mut fallbacks = vec![];
|
||||
let lib_dir = lib_dir.unwrap();
|
||||
let mut executables = get_path_files();
|
||||
if alias.is_some() {
|
||||
let alias = alias.as_ref().unwrap();
|
||||
for command in alias.keys() {
|
||||
if executables.contains(command) {
|
||||
continue;
|
||||
}
|
||||
executables.push(command.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let path = lib_dir.split(path_env_sep()).collect::<Vec<&str>>();
|
||||
|
||||
for p in path {
|
||||
#[cfg(windows)]
|
||||
let p = path_convert(p);
|
||||
|
||||
let files = match std::fs::read_dir(p) {
|
||||
Ok(files) => files,
|
||||
Err(_) => continue,
|
||||
};
|
||||
for file in files {
|
||||
let file = file.unwrap();
|
||||
let file_name = file.file_name().into_string().unwrap();
|
||||
let file_path = file.path();
|
||||
|
||||
if file_name.starts_with("_pay-respects-module-") {
|
||||
modules.push(file_path.to_string_lossy().to_string());
|
||||
} else if file_name.starts_with("_pay-respects-fallback-") {
|
||||
fallbacks.push(file_path.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modules.sort_unstable();
|
||||
fallbacks.sort_unstable();
|
||||
|
||||
(executables, modules, fallbacks)
|
||||
};
|
||||
}
|
||||
|
||||
let builtins = builtin_commands(&shell);
|
||||
executables.extend(builtins.clone());
|
||||
executables = executables.iter().unique().cloned().collect();
|
||||
let config = load_config();
|
||||
|
||||
let mut init = Data {
|
||||
shell,
|
||||
env: None,
|
||||
command,
|
||||
suggest: None,
|
||||
candidates: vec![],
|
||||
alias,
|
||||
split: vec![],
|
||||
privilege: None,
|
||||
error: "".to_string(),
|
||||
executables,
|
||||
modules,
|
||||
fallbacks,
|
||||
config,
|
||||
mode,
|
||||
};
|
||||
|
||||
init.split();
|
||||
init.extract_env();
|
||||
init.expand_command();
|
||||
if init.mode != Mode::Cnf {
|
||||
init.update_error(None);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
eprintln!("/// data initialization");
|
||||
eprintln!("shell: {}", init.shell);
|
||||
eprintln!("env: {:?}", init.env);
|
||||
eprintln!("command: {}", init.command);
|
||||
eprintln!("error: {}", init.error);
|
||||
eprintln!("modules: {:?}", init.modules);
|
||||
eprintln!("fallbacks: {:?}", init.fallbacks);
|
||||
}
|
||||
|
||||
init
|
||||
}
|
||||
|
||||
pub fn expand_command(&mut self) {
|
||||
if self.alias.is_none() {
|
||||
return;
|
||||
}
|
||||
let alias = self.alias.as_ref().unwrap();
|
||||
if let Some(command) = expand_alias_multiline(alias, &self.command) {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("expand_command: {}", command);
|
||||
self.update_command(&command);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_suggest(&mut self) {
|
||||
if self.alias.is_none() {
|
||||
return;
|
||||
}
|
||||
let alias = self.alias.as_ref().unwrap();
|
||||
if let Some(suggest) = expand_alias_multiline(alias, self.suggest.as_ref().unwrap()) {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("expand_suggest: {}", suggest);
|
||||
self.update_suggest(&suggest);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split(&mut self) {
|
||||
self.extract_privilege();
|
||||
let split = split_command(&self.command);
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("split: {:?}", split);
|
||||
if split.is_empty() {
|
||||
eprintln!("{}", t!("empty-command"));
|
||||
exit(1);
|
||||
}
|
||||
self.split = split;
|
||||
}
|
||||
|
||||
pub fn extract_privilege(&mut self) {
|
||||
let command = {
|
||||
let first = self.command.split_whitespace().next();
|
||||
if let Some(first) = first {
|
||||
first.to_string()
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Some(sudo) = self.config.sudo.as_ref() {
|
||||
if command == *sudo {
|
||||
self.privilege = Some(command.to_string());
|
||||
self.command = self.command.replacen(sudo, "", 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if PRIVILEGE_LIST.contains(&command.as_str()) {
|
||||
self.privilege = Some(command.to_string());
|
||||
self.command = self.command.replacen(&self.split[0], "", 1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_env(&mut self) {
|
||||
let mut envs = vec![];
|
||||
loop {
|
||||
let mut char = self.split[0].char_indices();
|
||||
char.next();
|
||||
let offset = char.offset();
|
||||
if self.split[0][offset..].contains("=") {
|
||||
envs.push(self.split.remove(0));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !envs.is_empty() {
|
||||
self.env = Some(envs.join(" "));
|
||||
self.command = self.split.join(" ");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_error(&mut self, error: Option<String>) {
|
||||
if let Some(error) = error {
|
||||
self.error = error
|
||||
.to_lowercase()
|
||||
.split_whitespace()
|
||||
.collect::<Vec<&str>>()
|
||||
.join(" ");
|
||||
} else {
|
||||
self.error = get_error(&self.shell, &self.command, self);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_command(&mut self, command: &str) {
|
||||
self.command = command.to_string();
|
||||
self.split();
|
||||
}
|
||||
|
||||
pub fn update_suggest(&mut self, suggest: &str) {
|
||||
let split = split_command(suggest);
|
||||
if PRIVILEGE_LIST.contains(&split[0].as_str()) {
|
||||
self.suggest = Some(suggest.replacen(&split[0], "", 1));
|
||||
self.privilege = Some(split[0].clone())
|
||||
} else {
|
||||
self.suggest = Some(suggest.to_string());
|
||||
};
|
||||
}
|
||||
}
|
17
core/src/init.rs
Normal file
17
core/src/init.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
pub struct Init {
|
||||
pub shell: String,
|
||||
pub binary_path: String,
|
||||
pub alias: String,
|
||||
pub cnf: bool,
|
||||
}
|
||||
|
||||
impl Init {
|
||||
pub fn new() -> Init {
|
||||
Init {
|
||||
shell: String::from(""),
|
||||
binary_path: String::from(""),
|
||||
alias: String::from("f"),
|
||||
cnf: true,
|
||||
}
|
||||
}
|
||||
}
|
@@ -18,6 +18,9 @@ use std::env;
|
||||
use sys_locale::get_locale;
|
||||
|
||||
mod args;
|
||||
mod config;
|
||||
mod data;
|
||||
mod init;
|
||||
mod modes;
|
||||
mod rules;
|
||||
mod shell;
|
||||
@@ -51,7 +54,7 @@ fn main() -> Result<(), std::io::Error> {
|
||||
init.ok().unwrap()
|
||||
};
|
||||
|
||||
use shell::Mode::*;
|
||||
use data::Mode::*;
|
||||
match data.mode {
|
||||
Suggestion => modes::suggestion(&mut data),
|
||||
Echo => modes::echo(&mut data),
|
||||
@@ -62,7 +65,7 @@ fn main() -> Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init() -> Result<shell::Data, args::Status> {
|
||||
fn init() -> Result<data::Data, args::Status> {
|
||||
let locale = {
|
||||
let sys_locale = {
|
||||
// use terminal locale if available
|
||||
@@ -95,5 +98,5 @@ fn init() -> Result<shell::Data, args::Status> {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(shell::Data::init())
|
||||
Ok(data::Data::init())
|
||||
}
|
||||
|
@@ -6,7 +6,8 @@ use ui::Color;
|
||||
use pay_respects_utils::evals::best_matches_path;
|
||||
use pay_respects_utils::files::best_match_file;
|
||||
|
||||
use crate::shell::{shell_evaluated_commands, Data};
|
||||
use crate::data::Data;
|
||||
use crate::shell::shell_evaluated_commands;
|
||||
use crate::style::highlight_difference;
|
||||
use crate::suggestions;
|
||||
use crate::suggestions::suggest_candidates;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use crate::shell::Data;
|
||||
use crate::data::Data;
|
||||
use pay_respects_parser::parse_rules;
|
||||
use pay_respects_utils::evals::*;
|
||||
|
||||
|
@@ -1,9 +1,4 @@
|
||||
use pay_respects_utils::evals::split_command;
|
||||
use pay_respects_utils::files::get_path_files;
|
||||
use pay_respects_utils::files::path_env_sep;
|
||||
|
||||
use askama::Template;
|
||||
use itertools::Itertools;
|
||||
|
||||
use std::process::{exit, Stdio};
|
||||
|
||||
@@ -15,269 +10,19 @@ use std::time::Duration;
|
||||
#[cfg(windows)]
|
||||
use pay_respects_utils::files::path_convert;
|
||||
|
||||
pub const PRIVILEGE_LIST: [&str; 2] = ["sudo", "doas"];
|
||||
use crate::data::{Data, Mode};
|
||||
use crate::init::Init;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum Mode {
|
||||
Suggestion,
|
||||
Echo,
|
||||
NoConfirm,
|
||||
Cnf,
|
||||
}
|
||||
pub struct Init {
|
||||
pub shell: String,
|
||||
pub binary_path: String,
|
||||
pub alias: String,
|
||||
pub cnf: bool,
|
||||
}
|
||||
|
||||
impl Init {
|
||||
pub fn new() -> Init {
|
||||
Init {
|
||||
shell: String::from(""),
|
||||
binary_path: String::from(""),
|
||||
alias: String::from("f"),
|
||||
cnf: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Data {
|
||||
pub shell: String,
|
||||
pub env: Option<String>,
|
||||
pub command: String,
|
||||
pub suggest: Option<String>,
|
||||
pub candidates: Vec<String>,
|
||||
pub split: Vec<String>,
|
||||
pub alias: Option<HashMap<String, String>>,
|
||||
pub privilege: Option<String>,
|
||||
pub error: String,
|
||||
pub executables: Vec<String>,
|
||||
pub modules: Vec<String>,
|
||||
pub fallbacks: Vec<String>,
|
||||
pub mode: Mode,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub fn init() -> Data {
|
||||
let shell = get_shell();
|
||||
let command = last_command(&shell).trim().to_string();
|
||||
let alias = alias_map(&shell);
|
||||
let mode = run_mode();
|
||||
let (mut executables, modules, fallbacks);
|
||||
let lib_dir = {
|
||||
if let Ok(lib_dir) = std::env::var("_PR_LIB") {
|
||||
Some(lib_dir)
|
||||
} else {
|
||||
option_env!("_DEF_PR_LIB").map(|dir| dir.to_string())
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("lib_dir: {:?}", lib_dir);
|
||||
|
||||
if lib_dir.is_none() {
|
||||
(executables, modules, fallbacks) = {
|
||||
let path_executables = get_path_files();
|
||||
let mut executables = vec![];
|
||||
let mut modules = vec![];
|
||||
let mut fallbacks = vec![];
|
||||
for exe in path_executables {
|
||||
if exe.starts_with("_pay-respects-module-") {
|
||||
modules.push(exe.to_string());
|
||||
} else if exe.starts_with("_pay-respects-fallback-") {
|
||||
fallbacks.push(exe.to_string());
|
||||
} else {
|
||||
executables.push(exe.to_string());
|
||||
}
|
||||
}
|
||||
modules.sort_unstable();
|
||||
fallbacks.sort_unstable();
|
||||
if alias.is_some() {
|
||||
let alias = alias.as_ref().unwrap();
|
||||
for command in alias.keys() {
|
||||
if executables.contains(command) {
|
||||
continue;
|
||||
}
|
||||
executables.push(command.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
(executables, modules, fallbacks)
|
||||
};
|
||||
} else {
|
||||
(executables, modules, fallbacks) = {
|
||||
let mut modules = vec![];
|
||||
let mut fallbacks = vec![];
|
||||
let lib_dir = lib_dir.unwrap();
|
||||
let mut executables = get_path_files();
|
||||
if alias.is_some() {
|
||||
let alias = alias.as_ref().unwrap();
|
||||
for command in alias.keys() {
|
||||
if executables.contains(command) {
|
||||
continue;
|
||||
}
|
||||
executables.push(command.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let path = lib_dir.split(path_env_sep()).collect::<Vec<&str>>();
|
||||
|
||||
for p in path {
|
||||
#[cfg(windows)]
|
||||
let p = path_convert(p);
|
||||
|
||||
let files = match std::fs::read_dir(p) {
|
||||
Ok(files) => files,
|
||||
Err(_) => continue,
|
||||
};
|
||||
for file in files {
|
||||
let file = file.unwrap();
|
||||
let file_name = file.file_name().into_string().unwrap();
|
||||
let file_path = file.path();
|
||||
|
||||
if file_name.starts_with("_pay-respects-module-") {
|
||||
modules.push(file_path.to_string_lossy().to_string());
|
||||
} else if file_name.starts_with("_pay-respects-fallback-") {
|
||||
fallbacks.push(file_path.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modules.sort_unstable();
|
||||
fallbacks.sort_unstable();
|
||||
|
||||
(executables, modules, fallbacks)
|
||||
};
|
||||
}
|
||||
|
||||
let builtins = builtin_commands(&shell);
|
||||
executables.extend(builtins.clone());
|
||||
executables = executables.iter().unique().cloned().collect();
|
||||
|
||||
let mut init = Data {
|
||||
shell,
|
||||
env: None,
|
||||
command,
|
||||
suggest: None,
|
||||
candidates: vec![],
|
||||
alias,
|
||||
split: vec![],
|
||||
privilege: None,
|
||||
error: "".to_string(),
|
||||
executables,
|
||||
modules,
|
||||
fallbacks,
|
||||
mode,
|
||||
};
|
||||
|
||||
init.split();
|
||||
init.extract_env();
|
||||
init.expand_command();
|
||||
if init.mode != Mode::Cnf {
|
||||
init.update_error(None);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
eprintln!("/// data initialization");
|
||||
eprintln!("shell: {}", init.shell);
|
||||
eprintln!("env: {:?}", init.env);
|
||||
eprintln!("command: {}", init.command);
|
||||
eprintln!("error: {}", init.error);
|
||||
eprintln!("modules: {:?}", init.modules);
|
||||
eprintln!("fallbacks: {:?}", init.fallbacks);
|
||||
}
|
||||
|
||||
init
|
||||
}
|
||||
|
||||
pub fn expand_command(&mut self) {
|
||||
if self.alias.is_none() {
|
||||
return;
|
||||
}
|
||||
let alias = self.alias.as_ref().unwrap();
|
||||
if let Some(command) = expand_alias_multiline(alias, &self.command) {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("expand_command: {}", command);
|
||||
self.update_command(&command);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_suggest(&mut self) {
|
||||
if self.alias.is_none() {
|
||||
return;
|
||||
}
|
||||
let alias = self.alias.as_ref().unwrap();
|
||||
if let Some(suggest) = expand_alias_multiline(alias, self.suggest.as_ref().unwrap()) {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("expand_suggest: {}", suggest);
|
||||
self.update_suggest(&suggest);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split(&mut self) {
|
||||
let mut split = split_command(&self.command);
|
||||
if PRIVILEGE_LIST.contains(&split[0].as_str()) {
|
||||
self.command = self.command.replacen(&split[0], "", 1).trim().to_string();
|
||||
self.privilege = Some(split.remove(0))
|
||||
}
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("split: {:?}", split);
|
||||
if split.is_empty() {
|
||||
eprintln!("{}", t!("empty-command"));
|
||||
exit(1);
|
||||
}
|
||||
self.split = split;
|
||||
}
|
||||
|
||||
pub fn extract_env(&mut self) {
|
||||
let mut envs = vec![];
|
||||
loop {
|
||||
let mut char = self.split[0].char_indices();
|
||||
char.next();
|
||||
let offset = char.offset();
|
||||
if self.split[0][offset..].contains("=") {
|
||||
envs.push(self.split.remove(0));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !envs.is_empty() {
|
||||
self.env = Some(envs.join(" "));
|
||||
self.command = self.split.join(" ");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_error(&mut self, error: Option<String>) {
|
||||
if let Some(error) = error {
|
||||
self.error = error
|
||||
.to_lowercase()
|
||||
.split_whitespace()
|
||||
.collect::<Vec<&str>>()
|
||||
.join(" ");
|
||||
} else {
|
||||
self.error = get_error(&self.shell, &self.command);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_command(&mut self, command: &str) {
|
||||
self.command = command.to_string();
|
||||
self.split();
|
||||
}
|
||||
|
||||
pub fn update_suggest(&mut self, suggest: &str) {
|
||||
let split = split_command(suggest);
|
||||
if PRIVILEGE_LIST.contains(&split[0].as_str()) {
|
||||
self.suggest = Some(suggest.replacen(&split[0], "", 1));
|
||||
self.privilege = Some(split[0].clone())
|
||||
} else {
|
||||
self.suggest = Some(suggest.to_string());
|
||||
};
|
||||
}
|
||||
}
|
||||
const PRIVILEGE_LIST: [&str; 2] = ["sudo", "doas"];
|
||||
|
||||
pub fn elevate(data: &mut Data, command: &mut String) {
|
||||
if is_privileged(command, data) {
|
||||
return;
|
||||
}
|
||||
if data.config.sudo.is_some() {
|
||||
*command = format!("{} {}", data.config.sudo.as_ref().unwrap(), command);
|
||||
return;
|
||||
}
|
||||
for privilege in PRIVILEGE_LIST.iter() {
|
||||
if data.executables.contains(&privilege.to_string()) {
|
||||
*command = format!("{} {}", privilege, command);
|
||||
@@ -286,6 +31,13 @@ pub fn elevate(data: &mut Data, command: &mut String) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_privileged(command: &str, data: &Data) -> bool {
|
||||
if data.config.sudo.is_some() {
|
||||
return command == data.config.sudo.as_ref().unwrap();
|
||||
}
|
||||
PRIVILEGE_LIST.contains(&command)
|
||||
}
|
||||
|
||||
pub fn add_candidates_no_dup(
|
||||
command: &str,
|
||||
candidates: &mut Vec<String>,
|
||||
@@ -302,13 +54,15 @@ pub fn add_candidates_no_dup(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_error(shell: &str, command: &str) -> String {
|
||||
pub fn get_error(shell: &str, command: &str, data: &Data) -> String {
|
||||
let error_msg = std::env::var("_PR_ERROR_MSG");
|
||||
let error = if let Ok(error_msg) = error_msg {
|
||||
std::env::remove_var("_PR_ERROR_MSG");
|
||||
error_msg
|
||||
} else {
|
||||
error_output_threaded(shell, command)
|
||||
let timeout = data.config.timeout.0;
|
||||
eprintln!("time out is: {}", timeout);
|
||||
error_output_threaded(shell, command, timeout)
|
||||
};
|
||||
error
|
||||
.to_lowercase()
|
||||
@@ -317,7 +71,7 @@ pub fn get_error(shell: &str, command: &str) -> String {
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
pub fn error_output_threaded(shell: &str, command: &str) -> String {
|
||||
pub fn error_output_threaded(shell: &str, command: &str, timeout: u64) -> String {
|
||||
let (sender, receiver) = channel();
|
||||
|
||||
thread::scope(|s| {
|
||||
@@ -334,7 +88,7 @@ pub fn error_output_threaded(shell: &str, command: &str) -> String {
|
||||
.expect("failed to send output");
|
||||
});
|
||||
|
||||
match receiver.recv_timeout(Duration::from_secs(3)) {
|
||||
match receiver.recv_timeout(Duration::from_millis(timeout)) {
|
||||
Ok(output) => match output.stderr.is_empty() {
|
||||
true => String::from_utf8_lossy(&output.stdout).to_string(),
|
||||
false => String::from_utf8_lossy(&output.stderr).to_string(),
|
||||
@@ -630,7 +384,8 @@ pub fn get_shell() -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn builtin_commands(shell: &str) -> Vec<String> {
|
||||
#[allow(unused_variables)]
|
||||
pub fn builtin_commands(shell: &str) -> Vec<String> {
|
||||
// TODO: add the commands for each shell
|
||||
// these should cover most of the builtin commands
|
||||
// (maybe with false positives)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
use crate::shell::Data;
|
||||
use crate::shell::PRIVILEGE_LIST;
|
||||
use crate::data::Data;
|
||||
use crate::shell::is_privileged;
|
||||
use colored::*;
|
||||
use pay_respects_utils::evals::split_command;
|
||||
|
||||
@@ -19,7 +19,7 @@ pub fn highlight_difference(data: &Data, suggested_command: &str) -> Option<Stri
|
||||
return None;
|
||||
}
|
||||
|
||||
let privileged = PRIVILEGE_LIST.contains(&split_suggested_command[0].as_str());
|
||||
let privileged = is_privileged(&split_suggested_command[0], data);
|
||||
|
||||
let mut old_entries = Vec::new();
|
||||
for command in &split_suggested_command {
|
||||
|
@@ -7,10 +7,9 @@ use colored::Colorize;
|
||||
use inquire::*;
|
||||
use ui::Color;
|
||||
|
||||
use crate::data::Data;
|
||||
use crate::rules::match_pattern;
|
||||
use crate::shell::{
|
||||
add_candidates_no_dup, module_output, shell_evaluated_commands, shell_syntax, Data,
|
||||
};
|
||||
use crate::shell::{add_candidates_no_dup, module_output, shell_evaluated_commands, shell_syntax};
|
||||
use crate::style::highlight_difference;
|
||||
|
||||
pub fn suggest_candidates(data: &mut Data) {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
use crate::data::Data;
|
||||
use crate::shell::command_output_or_error;
|
||||
use crate::shell::{command_output, elevate, Data};
|
||||
use crate::shell::{command_output, elevate};
|
||||
use colored::Colorize;
|
||||
use std::io::stderr;
|
||||
use std::process::Command;
|
||||
|
@@ -73,8 +73,7 @@ pub async fn ai_suggestion(last_command: &str, error_msg: &str, locale: &str) {
|
||||
map.insert("error_msg", error_msg);
|
||||
|
||||
let user_locale = {
|
||||
let locale = std::env::var("_PR_AI_LOCALE")
|
||||
.unwrap_or_else(|_| locale.to_string());
|
||||
let locale = std::env::var("_PR_AI_LOCALE").unwrap_or_else(|_| locale.to_string());
|
||||
if locale.len() < 2 {
|
||||
"en-US".to_string()
|
||||
} else {
|
||||
|
@@ -35,6 +35,12 @@ fn main() -> Result<(), std::io::Error> {
|
||||
}
|
||||
|
||||
rules::runtime_match(&executable, &shell, &last_command, &error_msg, &executables);
|
||||
rules::runtime_match("_PR_GENERAL", &shell, &last_command, &error_msg, &executables);
|
||||
rules::runtime_match(
|
||||
"_PR_GENERAL",
|
||||
&shell,
|
||||
&last_command,
|
||||
&error_msg,
|
||||
&executables,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
Reference in New Issue
Block a user