add actions derive macro
This commit is contained in:
@@ -0,0 +1,79 @@
|
|||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::DeriveInput;
|
||||||
|
use syn::__private::TokenStream2;
|
||||||
|
|
||||||
|
pub fn action(input: TokenStream) -> TokenStream {
|
||||||
|
let input = syn::parse::<DeriveInput>(input).unwrap();
|
||||||
|
|
||||||
|
let name = &input.ident;
|
||||||
|
|
||||||
|
let enum_data = if let syn::Data::Enum(enum_data) = &input.data {
|
||||||
|
enum_data
|
||||||
|
} else {
|
||||||
|
panic!("`Register` can only be used on enums");
|
||||||
|
};
|
||||||
|
|
||||||
|
let variant_description = generate_variant_description(&enum_data);
|
||||||
|
let variant_constants = generate_variant_constants(&name, &enum_data);
|
||||||
|
|
||||||
|
let expanded = compile_output(name, variant_description, variant_constants);
|
||||||
|
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_variant_description(
|
||||||
|
enum_data: &syn::DataEnum,
|
||||||
|
) -> impl Iterator<Item = TokenStream2> + '_ {
|
||||||
|
let variant_matches_backend = enum_data.variants.iter().map(|variant| {
|
||||||
|
let variant_name = &variant.ident;
|
||||||
|
let variant_lowercase =
|
||||||
|
proc_macro2::Literal::string(&variant_name.to_string().to_lowercase());
|
||||||
|
quote! {
|
||||||
|
Self::#variant_name => #variant_lowercase,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
variant_matches_backend
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_variant_constants<'a>(
|
||||||
|
name: &'a syn::Ident,
|
||||||
|
enum_data: &'a syn::DataEnum,
|
||||||
|
) -> impl Iterator<Item = TokenStream2> + 'a {
|
||||||
|
let result = enum_data.variants.iter().map(move |variant| {
|
||||||
|
let variant_name = &variant.ident;
|
||||||
|
|
||||||
|
let variant_uppercase = proc_macro2::Ident::new(
|
||||||
|
&variant_name.to_string().to_uppercase(),
|
||||||
|
proc_macro2::Span::call_site(),
|
||||||
|
);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
pub(crate) const #variant_uppercase: &str = #name::#variant_name.name();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_output<T, U>(
|
||||||
|
name: &syn::Ident,
|
||||||
|
variant_description: T,
|
||||||
|
variant_constants: U,
|
||||||
|
) -> TokenStream2
|
||||||
|
where
|
||||||
|
T: Iterator<Item = TokenStream2>,
|
||||||
|
U: Iterator<Item = TokenStream2>,
|
||||||
|
{
|
||||||
|
let expanded = quote! {
|
||||||
|
impl #name {
|
||||||
|
pub const fn name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
#(#variant_description)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#(#variant_constants)*
|
||||||
|
};
|
||||||
|
expanded
|
||||||
|
}
|
||||||
+7
-108
@@ -1,115 +1,14 @@
|
|||||||
|
mod action;
|
||||||
|
mod register;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
|
||||||
use syn::DeriveInput;
|
|
||||||
use syn::__private::TokenStream2;
|
|
||||||
|
|
||||||
#[proc_macro_derive(Register)]
|
#[proc_macro_derive(Register)]
|
||||||
pub fn register(input: TokenStream) -> TokenStream {
|
pub fn register(input: TokenStream) -> TokenStream {
|
||||||
let input = syn::parse::<DeriveInput>(input).unwrap();
|
register::register(input)
|
||||||
|
|
||||||
let name = &input.ident;
|
|
||||||
|
|
||||||
let enum_data = if let syn::Data::Enum(enum_data) = &input.data {
|
|
||||||
enum_data
|
|
||||||
} else {
|
|
||||||
panic!("`Register` can only be used on enums");
|
|
||||||
};
|
|
||||||
|
|
||||||
let first_variant = &enum_data.variants[0].ident;
|
|
||||||
|
|
||||||
let variant_matches_backend = generate_variant_backend(enum_data);
|
|
||||||
let variant_matches_next = generate_variant_matches_next(enum_data);
|
|
||||||
let variant_imports = generate_variant_imports(enum_data);
|
|
||||||
|
|
||||||
let expanded = compile_output(
|
|
||||||
name,
|
|
||||||
first_variant,
|
|
||||||
variant_matches_backend,
|
|
||||||
variant_matches_next,
|
|
||||||
variant_imports,
|
|
||||||
);
|
|
||||||
|
|
||||||
TokenStream::from(expanded)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_output<T, U, V>(
|
#[proc_macro_derive(Action)]
|
||||||
name: &syn::Ident,
|
pub fn action(input: TokenStream) -> TokenStream {
|
||||||
first_variant: &syn::Ident,
|
action::action(input)
|
||||||
variant_backend: T,
|
|
||||||
variant_next: U,
|
|
||||||
variant_imports: V,
|
|
||||||
) -> TokenStream2
|
|
||||||
where
|
|
||||||
T: Iterator<Item = TokenStream2>,
|
|
||||||
U: Iterator<Item = TokenStream2>,
|
|
||||||
V: Iterator<Item = TokenStream2>,
|
|
||||||
{
|
|
||||||
let expanded = quote! {
|
|
||||||
#(#variant_imports)*
|
|
||||||
|
|
||||||
impl #name {
|
|
||||||
pub fn iter() -> BackendIter {
|
|
||||||
BackendIter {
|
|
||||||
next: Some(Self::#first_variant),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn get_backend(&self) -> Box<dyn Backend> {
|
|
||||||
match self {
|
|
||||||
#(#variant_backend)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn next(&self) -> Option<Self> {
|
|
||||||
match self {
|
|
||||||
#(#variant_next)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
expanded
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_variant_imports(enum_data: &syn::DataEnum) -> impl Iterator<Item = TokenStream2> + '_ {
|
|
||||||
let variant_imports = enum_data.variants.iter().map(|variant| {
|
|
||||||
let variant_name = &variant.ident;
|
|
||||||
|
|
||||||
let variant_module = proc_macro2::Ident::new(
|
|
||||||
&variant_name.to_string().to_lowercase(),
|
|
||||||
proc_macro2::Span::call_site(),
|
|
||||||
);
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
pub(crate) use actual::#variant_module::#variant_name;
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
variant_imports
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_variant_matches_next(
|
|
||||||
enum_data: &syn::DataEnum,
|
|
||||||
) -> impl Iterator<Item = TokenStream2> + '_ {
|
|
||||||
let variant_matches_next = enum_data.variants.iter().enumerate().map(|(i, variant)| {
|
|
||||||
let variant_name = &variant.ident;
|
|
||||||
if i == enum_data.variants.len() - 1 {
|
|
||||||
quote! {
|
|
||||||
Self::#variant_name => None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let next_variant = &enum_data.variants[i + 1].ident;
|
|
||||||
quote! {
|
|
||||||
Self::#variant_name => Some(Self::#next_variant),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
variant_matches_next
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_variant_backend(enum_data: &syn::DataEnum) -> impl Iterator<Item = TokenStream2> + '_ {
|
|
||||||
let variant_matches_backend = enum_data.variants.iter().map(|variant| {
|
|
||||||
let variant_name = &variant.ident;
|
|
||||||
quote! {
|
|
||||||
Self::#variant_name => Box::new(#variant_name::new()),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
variant_matches_backend
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::DeriveInput;
|
||||||
|
use syn::__private::TokenStream2;
|
||||||
|
|
||||||
|
pub fn register(input: TokenStream) -> TokenStream {
|
||||||
|
let input = syn::parse::<DeriveInput>(input).unwrap();
|
||||||
|
|
||||||
|
let name = &input.ident;
|
||||||
|
|
||||||
|
let enum_data = if let syn::Data::Enum(enum_data) = &input.data {
|
||||||
|
enum_data
|
||||||
|
} else {
|
||||||
|
panic!("`Register` can only be used on enums");
|
||||||
|
};
|
||||||
|
|
||||||
|
let first_variant = &enum_data.variants[0].ident;
|
||||||
|
|
||||||
|
let variant_matches_backend = generate_variant_backend(enum_data);
|
||||||
|
let variant_matches_next = generate_variant_matches_next(enum_data);
|
||||||
|
let variant_imports = generate_variant_imports(enum_data);
|
||||||
|
|
||||||
|
let expanded = compile_output(
|
||||||
|
name,
|
||||||
|
first_variant,
|
||||||
|
variant_matches_backend,
|
||||||
|
variant_matches_next,
|
||||||
|
variant_imports,
|
||||||
|
);
|
||||||
|
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_output<T, U, V>(
|
||||||
|
name: &syn::Ident,
|
||||||
|
first_variant: &syn::Ident,
|
||||||
|
variant_backend: T,
|
||||||
|
variant_next: U,
|
||||||
|
variant_imports: V,
|
||||||
|
) -> TokenStream2
|
||||||
|
where
|
||||||
|
T: Iterator<Item = TokenStream2>,
|
||||||
|
U: Iterator<Item = TokenStream2>,
|
||||||
|
V: Iterator<Item = TokenStream2>,
|
||||||
|
{
|
||||||
|
let expanded = quote! {
|
||||||
|
#(#variant_imports)*
|
||||||
|
|
||||||
|
impl #name {
|
||||||
|
pub fn iter() -> BackendIter {
|
||||||
|
BackendIter {
|
||||||
|
next: Some(Self::#first_variant),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn get_backend(&self) -> Box<dyn Backend> {
|
||||||
|
match self {
|
||||||
|
#(#variant_backend)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn next(&self) -> Option<Self> {
|
||||||
|
match self {
|
||||||
|
#(#variant_next)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expanded
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_variant_imports(enum_data: &syn::DataEnum) -> impl Iterator<Item = TokenStream2> + '_ {
|
||||||
|
let variant_imports = enum_data.variants.iter().map(|variant| {
|
||||||
|
let variant_name = &variant.ident;
|
||||||
|
|
||||||
|
let variant_module = proc_macro2::Ident::new(
|
||||||
|
&variant_name.to_string().to_lowercase(),
|
||||||
|
proc_macro2::Span::call_site(),
|
||||||
|
);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
pub(crate) use actual::#variant_module::#variant_name;
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
variant_imports
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_variant_matches_next(
|
||||||
|
enum_data: &syn::DataEnum,
|
||||||
|
) -> impl Iterator<Item = TokenStream2> + '_ {
|
||||||
|
let variant_matches_next = enum_data.variants.iter().enumerate().map(|(i, variant)| {
|
||||||
|
let variant_name = &variant.ident;
|
||||||
|
if i == enum_data.variants.len() - 1 {
|
||||||
|
quote! {
|
||||||
|
Self::#variant_name => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let next_variant = &enum_data.variants[i + 1].ident;
|
||||||
|
quote! {
|
||||||
|
Self::#variant_name => Some(Self::#next_variant),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
variant_matches_next
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_variant_backend(enum_data: &syn::DataEnum) -> impl Iterator<Item = TokenStream2> + '_ {
|
||||||
|
let variant_matches_backend = enum_data.variants.iter().map(|variant| {
|
||||||
|
let variant_name = &variant.ident;
|
||||||
|
quote! {
|
||||||
|
Self::#variant_name => Box::new(#variant_name::new()),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
variant_matches_backend
|
||||||
|
}
|
||||||
+16
-11
@@ -1,11 +1,16 @@
|
|||||||
pub(crate) const CLEAN: &str = "clean";
|
use pacdef_macro::Action;
|
||||||
pub(crate) const EDIT: &str = "edit";
|
|
||||||
pub(crate) const GROUPS: &str = "groups";
|
#[derive(Debug, Action)]
|
||||||
pub(crate) const IMPORT: &str = "import";
|
pub(crate) enum Actions {
|
||||||
pub(crate) const NEW: &str = "new";
|
Clean,
|
||||||
pub(crate) const REMOVE: &str = "remove";
|
Edit,
|
||||||
pub(crate) const SEARCH: &str = "search";
|
Groups,
|
||||||
pub(crate) const SHOW: &str = "show";
|
Import,
|
||||||
pub(crate) const SYNC: &str = "sync";
|
New,
|
||||||
pub(crate) const UNMANAGED: &str = "unmanaged";
|
Remove,
|
||||||
pub(crate) const VERSION: &str = "version";
|
Search,
|
||||||
|
Show,
|
||||||
|
Sync,
|
||||||
|
Unmanaged,
|
||||||
|
Version,
|
||||||
|
}
|
||||||
|
|||||||
+13
-19
@@ -6,7 +6,7 @@ use std::path::PathBuf;
|
|||||||
use anyhow::{ensure, Context, Result};
|
use anyhow::{ensure, Context, Result};
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
|
||||||
use crate::action;
|
use crate::action::*;
|
||||||
use crate::args;
|
use crate::args;
|
||||||
use crate::backend::{Backend, Backends, ToDoPerBackend};
|
use crate::backend::{Backend, Backends, ToDoPerBackend};
|
||||||
use crate::cmd::run_edit_command;
|
use crate::cmd::run_edit_command;
|
||||||
@@ -21,6 +21,7 @@ pub struct Pacdef {
|
|||||||
groups: HashSet<Group>,
|
groups: HashSet<Group>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO review
|
||||||
impl Pacdef {
|
impl Pacdef {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(args: ArgMatches, groups: HashSet<Group>) -> Self {
|
pub fn new(args: ArgMatches, groups: HashSet<Group>) -> Self {
|
||||||
@@ -29,27 +30,20 @@ impl Pacdef {
|
|||||||
|
|
||||||
#[allow(clippy::unit_arg)]
|
#[allow(clippy::unit_arg)]
|
||||||
pub fn run_action_from_arg(self) -> Result<()> {
|
pub fn run_action_from_arg(self) -> Result<()> {
|
||||||
// TODO review
|
|
||||||
match self.args.subcommand() {
|
match self.args.subcommand() {
|
||||||
Some((action::CLEAN, _)) => Ok(self.clean_packages()),
|
Some((CLEAN, _)) => Ok(self.clean_packages()),
|
||||||
Some((action::EDIT, groups)) => {
|
Some((EDIT, args)) => self.edit_group_files(args).context("editing group files"),
|
||||||
self.edit_group_files(groups).context("editing group files")
|
Some((GROUPS, _)) => Ok(self.show_groups()),
|
||||||
}
|
Some((IMPORT, args)) => self.import_groups(args).context("importing groups"),
|
||||||
Some((action::GROUPS, _)) => Ok(self.show_groups()),
|
Some((NEW, args)) => self.new_groups(args).context("creating new group files"),
|
||||||
Some((action::IMPORT, files)) => self.import_groups(files).context("importing groups"),
|
Some((REMOVE, args)) => self.remove_groups(args).context("removing groups"),
|
||||||
Some((action::NEW, files)) => {
|
Some((SHOW, args)) => self.show_group_content(args).context("showing groups"),
|
||||||
self.new_groups(files).context("creating new group files")
|
Some((SEARCH, args)) => {
|
||||||
}
|
|
||||||
Some((action::REMOVE, groups)) => self.remove_groups(groups).context("removing groups"),
|
|
||||||
Some((action::SHOW, groups)) => {
|
|
||||||
self.show_group_content(groups).context("showing groups")
|
|
||||||
}
|
|
||||||
Some((action::SEARCH, args)) => {
|
|
||||||
search::search_packages(args, &self.groups).context("searching packages")
|
search::search_packages(args, &self.groups).context("searching packages")
|
||||||
}
|
}
|
||||||
Some((action::SYNC, _)) => Ok(self.install_packages()),
|
Some((SYNC, _)) => Ok(self.install_packages()),
|
||||||
Some((action::UNMANAGED, _)) => Ok(self.show_unmanaged_packages()),
|
Some((UNMANAGED, _)) => Ok(self.show_unmanaged_packages()),
|
||||||
Some((action::VERSION, _)) => Ok(self.show_version()),
|
Some((VERSION, _)) => Ok(self.show_version()),
|
||||||
Some((_, _)) => todo!(),
|
Some((_, _)) => todo!(),
|
||||||
None => {
|
None => {
|
||||||
unreachable!("argument parser requires some subcommand to return an `ArgMatches`")
|
unreachable!("argument parser requires some subcommand to return an `ArgMatches`")
|
||||||
|
|||||||
Reference in New Issue
Block a user