Skip to content

Commit

Permalink
Merge pull request #401 from jewlexx/help-hook
Browse files Browse the repository at this point in the history
sfsu: Add ability to rename hooks or commands
  • Loading branch information
jewlexx authored Apr 9, 2024
2 parents a6e2d81 + ebc1ba2 commit 11da138
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 51 deletions.
95 changes: 85 additions & 10 deletions crates/derive/src/hooks.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,36 @@
use proc_macro2::{Ident, Span, TokenStream};
use proc_macro_crate::{crate_name, FoundCrate};
use proc_macro_error::abort_call_site;
use quote::{quote, ToTokens};
use quote::quote;
use syn::DeriveInput;

pub struct Variant {
pub name: Ident,
pub command_name: String,
pub hook_name: String,
}

impl Variant {
pub fn into_tuple(self) -> (Ident, String, String) {
(self.name, self.command_name, self.hook_name)
}

pub fn unzip(variants: impl Iterator<Item = Self>) -> (Vec<Ident>, Vec<String>, Vec<String>) {
let mut names = Vec::new();
let mut command_names = Vec::new();
let mut hook_names = Vec::new();

for variant in variants {
let (name, command_name, hook_name) = variant.into_tuple();
names.push(name);
command_names.push(command_name);
hook_names.push(hook_name);
}

(names, command_names, hook_names)
}
}

pub fn hook_enum(input: DeriveInput) -> TokenStream {
let struct_name = {
let original_ident = &input.ident;
Expand All @@ -18,23 +45,52 @@ pub fn hook_enum(input: DeriveInput) -> TokenStream {
.variants
.iter()
.filter_map(|variant| {
if variant.attrs.iter().any(|attr| match attr.meta {
// TODO: Refactor this to use one attribute i.e #[hook(...)]
let attrs = &variant.attrs;
if attrs.iter().any(|attr| match attr.meta {
syn::Meta::Path(ref p) => p.is_ident("no_hook"),
_ => false,
}) {
None
} else {
Some(variant.ident.to_token_stream())
let mut variant = Variant {
name: variant.ident.clone(),
command_name: heck::AsKebabCase(variant.ident.to_string()).to_string(),
hook_name: heck::AsKebabCase(variant.ident.to_string()).to_string(),
};

for attr in attrs.iter() {
if let syn::Meta::NameValue(ref nv) = attr.meta {
if nv.path.is_ident("hook_name") {
if let syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(ref s),
..
}) = nv.value
{
variant.hook_name = s.value();
}
}

if nv.path.is_ident("command_name") {
if let syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(ref s),
..
}) = nv.value
{
variant.command_name = s.value();
}
}
}
}

Some(variant)
}
})
.collect::<Vec<_>>(),
_ => abort_call_site!("Can only be derived for enums"),
};

let command_names = variants
.iter()
.map(|variant| heck::AsKebabCase(variant.to_string()).to_string())
.collect::<Vec<_>>();
let (variants, command_names, hook_names) = Variant::unzip(variants.into_iter());

let strum = match crate_name("strum").expect("strum is present in `Cargo.toml`") {
FoundCrate::Itself => Ident::new("strum", Span::call_site()),
Expand All @@ -43,16 +99,35 @@ pub fn hook_enum(input: DeriveInput) -> TokenStream {

quote! {
// TODO: Better way of doing this? or add support for meta in proc macro
#[derive(Debug, Clone, #strum::Display, #strum::IntoStaticStr, #strum::EnumIter, PartialEq, Eq)]
#[strum(serialize_all = "kebab-case")]
#[derive(Debug, Copy, Clone, #strum::EnumIter, PartialEq, Eq)]
pub enum #struct_name {
#(#variants),*
}

impl #struct_name {
pub fn command<'a>(self) -> &'a str {
match self {
#(#struct_name::#variants => #command_names,)*
}
}

pub fn hook<'a>(self) -> &'a str {
match self {
#(#struct_name::#variants => #hook_names,)*
}
}
}

// impl std::fmt::Display for #struct_name {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// write!(f, "{}", self.hook())
// }
// }

impl From<String> for #struct_name {
fn from(string: String) -> Self {
match string.as_str() {
#(#command_names => #struct_name::#variants,)*
#(stringify!(#hook_names) => #struct_name::#variants,)*
_ => panic!("Invalid command name: {}", string),
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub fn derive_into_inner(input: TokenStream) -> TokenStream {
inner::into_inner(parse_macro_input!(input as DeriveInput)).into()
}

#[proc_macro_derive(Hooks, attributes(no_hook))]
#[proc_macro_derive(Hooks, attributes(no_hook, hook_name, command_name))]
#[proc_macro_error]
pub fn derive_hook_enum(input: TokenStream) -> TokenStream {
hooks::hook_enum(parse_macro_input!(input as DeriveInput)).into()
Expand Down
7 changes: 0 additions & 7 deletions crates/derive/tests/helpers.rs

This file was deleted.

12 changes: 7 additions & 5 deletions crates/derive/tests/raw_enum.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
#![allow(dead_code)]

mod helpers;

use sfsu_derive::Hooks;

use helpers::enum_to_string;
use strum::IntoEnumIterator;

struct DummyStruct;

Expand All @@ -16,7 +14,9 @@ enum EnumWithData {

#[test]
fn has_all_variants() {
let variants = enum_to_string::<EnumWithDataHooks>();
let variants = EnumWithDataHooks::iter()
.map(|v| v.hook())
.collect::<String>();

assert_eq!(variants, "test1test2");
}
Expand All @@ -31,7 +31,9 @@ enum EnumExclude {

#[test]
fn excludes_no_hook_variant() {
let variants = enum_to_string::<EnumExcludeHooks>();
let variants = EnumExcludeHooks::iter()
.map(|v| v.hook())
.collect::<String>();

assert_eq!(variants, "test1test3");
}
1 change: 1 addition & 0 deletions crates/sprinkles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod git;
pub mod output;
pub mod packages;
pub mod progress;
pub mod shell;

mod opt;

Expand Down
45 changes: 45 additions & 0 deletions crates/sprinkles/src/shell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use clap::ValueEnum;
use strum::Display;

#[derive(Debug, Default, ValueEnum, Copy, Clone, Display, PartialEq, Eq)]
#[strum(serialize_all = "snake_case")]
pub enum Shell {
#[default]
Powershell,
Bash,
Zsh,
Nu,
}

impl Shell {
#[must_use]
pub fn config(self) -> ShellConfig {
ShellConfig::new(self)
}
}

#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
pub struct ShellConfig(Shell);

impl ShellConfig {
#[must_use]
pub fn new(shell: Shell) -> Self {
Self(shell)
}

#[must_use]
pub fn path(self) -> &'static str {
match self.0 {
Shell::Powershell => "$PROFILE",
Shell::Bash => "bashrc",
Shell::Zsh => "zshrc",
Shell::Nu => "$nu.config-path",
}
}
}

impl std::fmt::Display for ShellConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.path())
}
}
56 changes: 28 additions & 28 deletions src/commands/hook.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
use clap::{Parser, ValueEnum};
use itertools::Itertools;
use strum::{Display, IntoEnumIterator};

#[derive(Debug, Default, ValueEnum, Copy, Clone, Display, PartialEq, Eq)]
#[strum(serialize_all = "snake_case")]
enum Shell {
#[default]
Powershell,
Bash,
Zsh,
Nu,
}
use clap::Parser;
use sprinkles::shell::Shell;
use strum::IntoEnumIterator;

#[derive(Debug, Clone, Parser)]
pub struct Args {
Expand All @@ -24,6 +14,7 @@ pub struct Args {
impl super::Command for Args {
fn runner(self) -> Result<(), anyhow::Error> {
let shell = self.shell;
let shell_config = shell.config();
let enabled_hooks: Vec<super::CommandsHooks> = super::CommandsHooks::iter()
.filter(|variant| !self.disable.contains(variant))
.collect();
Expand All @@ -34,7 +25,11 @@ impl super::Command for Args {

// I would love to make this all one condition, but Powershell doesn't seem to support that elegantly
for command in enabled_hooks {
print!(" '{command}' {{ return sfsu.exe $args }} ");
print!(
" '{hook}' {{ return sfsu.exe {command} }} ",
hook = command.hook(),
command = command.command()
);
}

println!("default {{ scoop.ps1 @args }} }} }}");
Expand All @@ -44,35 +39,40 @@ impl super::Command for Args {
// println!("# Invoke-Expression (&sfsu hook)");
}
Shell::Bash | Shell::Zsh => {
let hook_list = enabled_hooks.iter().format(" | ");

println!(
"SCOOP_EXEC=$(which scoop) \n\
scoop () {{ \n\
case $1 in \n\
({hook_list}) sfsu.exe $@ ;; \n\
(*) $SCOOP_EXEC $@ ;; \n\
case $1 in"
);

for command in enabled_hooks {
println!(
"({hook}) sfsu.exe {command} ${{@:2}} ;;",
hook = command.hook(),
command = command.command()
);
}

println!(
"(*) $SCOOP_EXEC $@ ;; \n\
esac \n\
}} \n\n\
# Add the following to the end of your ~/.{} \n\
# source <(sfsu.exe hook --shell {shell})",
if shell == Shell::Bash {
"bashrc"
} else {
"zshrc"
}
# Add the following to the end of your ~/.{shell_config} \n\
# source <(sfsu.exe hook --shell {shell})"
);
}
Shell::Nu => {
for command in enabled_hooks {
println!(
"def --wrapped \"scoop {command}\" [...rest] {{ sfsu {command} ...$rest }}"
"def --wrapped \"scoop {hook}\" [...rest] {{ sfsu {command} ...$rest }}",
hook = command.hook(),
command = command.command()
);
}

println!(
"\n# To add this to your config, run `sfsu hook --shell {shell} | save ~/.cache/sfsu.nu`\n\
# And then in your main config add the following line to the end:\n\
# And then in your {shell_config} add the following line to the end:\n\
# source ~/.cache/sfsu.nu"
);
}
Expand Down

0 comments on commit 11da138

Please sign in to comment.