refactor!: initialization through templates

This commit is contained in:
iff 2025-04-05 18:31:45 +02:00
parent 4f637c1e1b
commit 885dc081e1
9 changed files with 230 additions and 211 deletions

64
Cargo.lock generated
View file

@ -23,6 +23,48 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]]
name = "askama"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a4e46abb203e00ef226442d452769233142bbfdd79c3941e84c8e61c4112543"
dependencies = [
"askama_derive",
"itoa",
"percent-encoding",
"serde",
"serde_json",
]
[[package]]
name = "askama_derive"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54398906821fd32c728135f7b351f0c7494ab95ae421d41b6f5a020e158f28a6"
dependencies = [
"askama_parser",
"basic-toml",
"memchr",
"proc-macro2",
"quote",
"rustc-hash",
"serde",
"serde_derive",
"syn",
]
[[package]]
name = "askama_parser"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f"
dependencies = [
"memchr",
"serde",
"serde_derive",
"winnow",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.4.0" version = "1.4.0"
@ -38,6 +80,15 @@ dependencies = [
"rustversion", "rustversion",
] ]
[[package]]
name = "basic-toml"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -462,6 +513,7 @@ dependencies = [
name = "pay-respects" name = "pay-respects"
version = "0.6.14" version = "0.6.14"
dependencies = [ dependencies = [
"askama",
"colored", "colored",
"inquire", "inquire",
"pay-respects-parser", "pay-respects-parser",
@ -514,6 +566,12 @@ dependencies = [
"regex-lite", "regex-lite",
] ]
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.32" version = "0.3.32"
@ -636,6 +694,12 @@ dependencies = [
"triomphe", "triomphe",
] ]
[[package]]
name = "rustc-hash"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "1.0.2" version = "1.0.2"

View file

@ -18,6 +18,7 @@ colored = "3"
sys-locale = "0.3" sys-locale = "0.3"
rust-i18n = "3" rust-i18n = "3"
regex-lite = "0.1" regex-lite = "0.1"
askama = "0.13"
inquire = "0.7" inquire = "0.7"

View file

@ -28,17 +28,13 @@ pub fn handle_args(args: impl IntoIterator<Item = String>) -> Status {
print_version(); print_version();
return Status::Exit; return Status::Exit;
} }
"-a" | "--alias" => { "-a" | "--alias" => match iter.peek() {
match iter.peek() {
Some(next_arg) if !next_arg.starts_with('-') => { Some(next_arg) if !next_arg.starts_with('-') => {
init.alias = next_arg.to_string(); init.alias = next_arg.to_string();
iter.next(); iter.next();
} }
_ => init.alias = String::from("f"), _ => init.alias = String::from("f"),
} },
init.auto_alias = true;
}
"--nocnf" => init.cnf = false, "--nocnf" => init.cnf = false,
_ => init.shell = arg, _ => init.shell = arg,
} }
@ -61,14 +57,14 @@ fn print_help() {
usage = "pay-respects <shell> [--alias [<alias>]] [--nocnf]", usage = "pay-respects <shell> [--alias [<alias>]] [--nocnf]",
eval = "Bash / Zsh / Fish".bold(), eval = "Bash / Zsh / Fish".bold(),
eval_examples = r#" eval_examples = r#"
eval "$(pay-respects bash --alias)" eval "$(pay-respects bash)"
eval "$(pay-respects zsh --alias)" eval "$(pay-respects zsh)"
pay-respects fish --alias | source pay-respects fish | source
"#, "#,
manual = "Nushell / PowerShell".bold(), manual = "Nushell / PowerShell".bold(),
manual_examples = r#" manual_examples = r#"
pay-respects nushell --alias pay-respects nushell
pay-respects pwsh --alias pay-respects pwsh
"# "#
) )
); );

View file

@ -2,6 +2,8 @@ use pay_respects_utils::evals::split_command;
use pay_respects_utils::files::get_path_files; use pay_respects_utils::files::get_path_files;
use pay_respects_utils::files::path_env_sep; use pay_respects_utils::files::path_env_sep;
use askama::Template;
use std::process::{exit, Stdio}; use std::process::{exit, Stdio};
use std::collections::HashMap; use std::collections::HashMap;
@ -24,7 +26,6 @@ pub struct Init {
pub shell: String, pub shell: String,
pub binary_path: String, pub binary_path: String,
pub alias: String, pub alias: String,
pub auto_alias: bool,
pub cnf: bool, pub cnf: bool,
} }
@ -34,7 +35,6 @@ impl Init {
shell: String::from(""), shell: String::from(""),
binary_path: String::from(""), binary_path: String::from(""),
alias: String::from("f"), alias: String::from("f"),
auto_alias: false,
cnf: true, cnf: true,
} }
} }
@ -219,7 +219,11 @@ impl Data {
pub fn update_error(&mut self, error: Option<String>) { pub fn update_error(&mut self, error: Option<String>) {
if let Some(error) = error { if let Some(error) = error {
self.error = error.to_lowercase().split_whitespace().collect::<Vec<&str>>().join(" "); self.error = error
.to_lowercase()
.split_whitespace()
.collect::<Vec<&str>>()
.join(" ");
} else { } else {
self.error = get_error(&self.shell, &self.command); self.error = get_error(&self.shell, &self.command);
} }
@ -271,7 +275,11 @@ pub fn get_error(shell: &str, command: &str) -> String {
} else { } else {
error_output_threaded(shell, command) error_output_threaded(shell, command)
}; };
error.to_lowercase().split_whitespace().collect::<Vec<&str>>().join(" ") error
.to_lowercase()
.split_whitespace()
.collect::<Vec<&str>>()
.join(" ")
} }
pub fn error_output_threaded(shell: &str, command: &str) -> String { pub fn error_output_threaded(shell: &str, command: &str) -> String {
@ -471,209 +479,82 @@ pub fn expand_alias_multiline(map: &HashMap<String, String>, command: &str) -> O
} }
pub fn initialization(init: &mut Init) { pub fn initialization(init: &mut Init) {
let last_command;
let shell_alias;
let alias = &init.alias; let alias = &init.alias;
let auto_alias = init.auto_alias;
let cnf = init.cnf; let cnf = init.cnf;
let binary_path = &init.binary_path; let binary_path = &init.binary_path;
match init.shell.as_str() {
"bash" => {
last_command = "$(fc -ln -1)";
shell_alias = "`alias`";
}
"zsh" => {
last_command = "$(fc -ln -1)";
shell_alias = "`alias`";
}
"fish" => {
last_command = "$(history | head -n 1)";
shell_alias = "$(alias)";
}
"nu" | "nush" | "nushell" => {
last_command = "(history | last).command";
shell_alias = r#"(help aliases | select name expansion | each ({ |row| $row.name + "=" + $row.expansion }) | str join (char nl))"#;
init.shell = "nu".to_string();
}
"pwsh" | "powershell" => {
last_command = "Get-History | Select-Object -Last 1 | ForEach-Object {$_.CommandLine}";
shell_alias = ";";
init.shell = "pwsh".to_string();
}
_ => {
println!("Unknown shell: {}", init.shell);
return;
}
}
let shell = &init.shell; let shell = &init.shell;
if init.shell == "nu" { #[derive(Template)]
let init = format!( #[template(path = "init.bash", escape = "none")]
r#" struct BashTemplate<'a> {
def --env {} [] {{ alias: &'a str,
let dir = (with-env {{ _PR_LAST_COMMAND: {}, _PR_ALIAS: {}, _PR_SHELL: nu }} {{ {} }}) binary_path: &'a str,
cd $dir cnf: bool,
}} }
"#, #[derive(Template)]
init.alias, last_command, shell_alias, init.binary_path #[template(path = "init.zsh", escape = "none")]
); struct ZshTemplate<'a> {
println!("{}", init); alias: &'a str,
return; binary_path: &'a str,
cnf: bool,
}
#[derive(Template)]
#[template(path = "init.fish", escape = "none")]
struct FishTemplate<'a> {
alias: &'a str,
binary_path: &'a str,
cnf: bool,
}
#[derive(Template)]
#[template(path = "init.ps1", escape = "none")]
struct PowershellTemplate<'a> {
alias: &'a str,
binary_path: &'a str,
cnf: bool,
}
#[derive(Template)]
#[template(path = "init.nu", escape = "none")]
struct NuTemplate<'a> {
alias: &'a str,
binary_path: &'a str,
} }
let mut initialize = match shell.as_str() { let initialize = match shell.as_str() {
"bash" | "zsh" | "fish" => format!( "bash" => BashTemplate {
"\ alias,
eval $(_PR_LAST_COMMAND=\"{}\" \ binary_path,
_PR_ALIAS=\"{}\" \ cnf,
_PR_SHELL=\"{}\" \ }
\"{}\")", .render()
last_command, shell_alias, shell, binary_path .unwrap(),
), "zsh" => ZshTemplate {
"pwsh" | "powershell" => format!( alias,
r#"& {{ binary_path,
try {{ cnf,
# fetch command and error from session history only when not in cnf mode }
if ($env:_PR_MODE -ne 'cnf') {{ .render()
$env:_PR_LAST_COMMAND = ({}); .unwrap(),
if ($PSVersionTable.PSVersion.Major -ge 7) {{ "fish" => FishTemplate {
$err = Get-Error; alias,
if ($env:_PR_LAST_COMMAND -eq $err.InvocationInfo.Line) {{ binary_path,
$env:_PR_ERROR_MSG = $err.Exception.Message cnf,
}} }
}} .render()
if ($env:_PR_LAST_COMMAND -eq $err.InvocationInfo.Line) {{ .unwrap(),
$env:_PR_ERROR_MSG = $err.Exception.Message "pwsh" | "powershell" | "ps" => PowershellTemplate {
}} alias,
}} binary_path,
$env:_PR_SHELL = '{}'; cnf,
&'{}'; }
}} .render()
finally {{ .unwrap(),
# restore mode from cnf "nu" | "nush" | "nushell" => NuTemplate { alias, binary_path }.render().unwrap(),
if ($env:_PR_MODE -eq 'cnf') {{
$env:_PR_MODE = $env:_PR_PWSH_ORIGIN_MODE;
$env:_PR_PWSH_ORIGIN_MODE = $null;
}}
}}
}}
"#,
last_command, shell, binary_path
),
_ => { _ => {
println!("Unsupported shell: {}", shell); eprintln!("{}: {}", t!("unknown-shell"), shell);
return; exit(1);
} }
}; };
if !auto_alias {
println!("{}", initialize);
return;
}
match shell.as_str() {
"bash" | "zsh" => {
initialize = format!(
r#"
alias {}='{}'
"#,
alias, initialize
);
}
"fish" => {
initialize = format!(
r#"
function {} -d "Suggest fixes to the previous command"
{}
end
"#,
alias, initialize
);
}
"pwsh" => {
initialize = format!(
"function {} {{\n{}",
alias,
initialize.split_once("\n").unwrap().1,
);
}
_ => {
println!("Unsupported shell: {}", shell);
return;
}
}
if cnf {
match shell.as_str() {
"bash" => {
initialize = format!(
r#"
command_not_found_handle() {{
eval $(_PR_LAST_COMMAND="$@" _PR_SHELL="{}" _PR_ALIAS="{}" _PR_MODE="cnf" "{}")
}}
{}
"#,
shell, shell_alias, binary_path, initialize
);
}
"zsh" => {
initialize = format!(
r#"
command_not_found_handler() {{
eval $(_PR_LAST_COMMAND="$@" _PR_SHELL="{}" _PR_ALIAS="{}" _PR_MODE="cnf" "{}")
}}
{}
"#,
shell, shell_alias, binary_path, initialize
);
}
"fish" => {
initialize = format!(
r#"
function fish_command_not_found --on-event fish_command_not_found
eval $(_PR_LAST_COMMAND="$argv" _PR_SHELL="{}" _PR_ALIAS="{}" _PR_MODE="cnf" "{}")
end
{}
"#,
shell, shell_alias, binary_path, initialize
);
}
"pwsh" => {
// usage with pwsh is limited as we cannot get arguments
// furthermore there is recursion
// initialize = format!(
// r#"{}
// $ExecutionContext.InvokeCommand.CommandNotFoundAction =
// {{
// param(
// [string]
// $commandName,
// [System.Management.Automation.CommandLookupEventArgs]
// $eventArgs
// )
// # powershell does not support run command with specific environment variables
// # but you must set global variables. so we are memorizing the current mode and the alias function will reset it later.
// $env:_PR_PWSH_ORIGIN_MODE=$env:_PR_MODE;
// $env:_PR_MODE='cnf';
// # powershell may search command with prefix 'get-' or '.\' first when this hook is hit, strip them
// $env:_PR_LAST_COMMAND=$commandName -replace '^get-|\.\\','';
// $eventArgs.Command = (Get-Command {});
// $eventArgs.StopSearch = $True;
// }}
// "#,
// initialize, alias
// )
}
_ => {
println!("Unsupported shell: {}", shell);
return;
}
}
}
println!("{}", initialize); println!("{}", initialize);
} }

7
core/templates/init.bash Normal file
View file

@ -0,0 +1,7 @@
alias {{ alias }}='eval $(_PR_LAST_COMMAND="$(fc -ln -1)" _PR_ALIAS="`alias`" _PR_SHELL="bash" "{{ binary_path }}")'
{%- if cnf %}
command_not_found_handle() {
eval $(_PR_LAST_COMMAND="$@" _PR_ALIAS="`alias`" _PR_SHELL="bash" _PR_MODE="cnf" "{{ binary_path }}")
}
{% endif %}

9
core/templates/init.fish Normal file
View file

@ -0,0 +1,9 @@
function {{ alias }} -d "Suggest fixes to the previous command"
eval $(_PR_LAST_COMMAND="$(history | head -n 1)" _PR_ALIAS="$(alias)" _PR_SHELL="fish" "{{ binary_path }}")
end
{%if cnf %}
function fish_command_not_found --on-event fish_command_not_found
eval $(_PR_LAST_COMMAND="$argv" _PR_ALIAS="$(alias)" _PR_SHELL="fish" _PR_MODE="cnf" "{{ binary_path }}")
end
{% endif %}

4
core/templates/init.nu Normal file
View file

@ -0,0 +1,4 @@
def --env {{ alias }} [] {
let dir = (with-env { _PR_LAST_COMMAND: (history | last).command, _PR_ALIAS: (help aliases | select name expansion | each ({ |row| $row.name + "=" + $row.expansion }) | str join (char nl)), _PR_SHELL: nu } { {{ binary_path }} })
cd $dir
}

50
core/templates/init.ps1 Normal file
View file

@ -0,0 +1,50 @@
function {{ alias }} {
try {
# fetch command and error from session history only when not in cnf mode
if ($env:_PR_MODE -ne 'cnf') {
$env:_PR_LAST_COMMAND = (Get-History | Select-Object -Last 1 | ForEach-Object {$_.CommandLine});
if ($PSVersionTable.PSVersion.Major -ge 7) {
$err = Get-Error;
if ($env:_PR_LAST_COMMAND -eq $err.InvocationInfo.Line) {
$env:_PR_ERROR_MSG = $err.Exception.Message
}
}
if ($env:_PR_LAST_COMMAND -eq $err.InvocationInfo.Line) {
$env:_PR_ERROR_MSG = $err.Exception.Message
}
}
$env:_PR_SHELL = 'pwsh';
&'{{ binary_path }}';
}
finally {
# restore mode from cnf
if ($env:_PR_MODE -eq 'cnf') {
$env:_PR_MODE = $env:_PR_PWSH_ORIGIN_MODE;
$env:_PR_PWSH_ORIGIN_MODE = $null;
}
}
}
{%- if cnf %}
# Uncomment this block to enable command not found hook
# It's not useful as we can't retrieve arguments,
# and it seems to be a recursion bug
# $ExecutionContext.InvokeCommand.CommandNotFoundAction =
# {
# param(
# [string]
# $commandName,
# [System.Management.Automation.CommandLookupEventArgs]
# $eventArgs
# )
# # powershell does not support run command with specific environment variables
# # but you must set global variables. so we are memorizing the current mode and the alias function will reset it later.
# $env:_PR_PWSH_ORIGIN_MODE=$env:_PR_MODE;
# $env:_PR_MODE='cnf';
# # powershell may search command with prefix 'get-' or '.\' first when this hook is hit, strip them
# $env:_PR_LAST_COMMAND=$commandName -replace '^get-|\.\\','';
# $eventArgs.Command = (Get-Command {{ alias }});
# $eventArgs.StopSearch = $True;
# }
{% endif %}

7
core/templates/init.zsh Normal file
View file

@ -0,0 +1,7 @@
alias {{ alias }}='eval $(_PR_LAST_COMMAND="$(fc -ln -1)" _PR_ALIAS="`alias`" _PR_SHELL="zsh" "{{ binary_path }}")'
{%- if cnf %}
command_not_found_handler() {
eval $(_PR_LAST_COMMAND="$@" _PR_SHELL="zsh" _PR_ALIAS="`alias`" _PR_MODE="cnf" "{{ binary_path }}")
}
{% endif %}