diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0df5c50..d286607 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,9 +1,17 @@ # Contributing -Thank you for considering to contribute to `pacdef`. The recommended workflow is this: +## General Steps + +Thank you for considering to contribute to `pacdef`. The recommended workflow is +this: 1. Open a github issue, mention that you would like to fix the issue in a PR. 2. Wait for approval. 3. Fork the repository and implement your fix / feature. 4. Make sure your code generates no warnings, and passes `rustfmt` and `clippy`. 5. Open the pull request. + +## Rust-Analyzer Issues + +Rust Analyzer may not work unless both the `pacutils` and `apt` packages are +installed. On Arch that is, this may vary on other distros. diff --git a/Cargo.lock b/Cargo.lock index 311873c..370e2f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,6 +215,18 @@ dependencies = [ "syn", ] +[[package]] +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -395,8 +407,8 @@ dependencies = [ "anyhow", "clap", "const_format", + "enum_dispatch", "libc", - "pacdef_macros", "path-absolutize", "regex", "rstest", @@ -409,15 +421,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "pacdef_macros" -version = "1.0.1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "path-absolutize" version = "3.1.1" diff --git a/Cargo.toml b/Cargo.toml index 055767b..3a0a3ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,7 @@ categories = ["command-line-utilities"] rust-version = "1.74" [workspace.dependencies] -pacdef_macros = { path = "crates/pacdef_macros", version = "1.0" } -pacdef = { path = "crates/pacdef", version = "1.6" } +pacdef = { path = "crates/pacdef" } [profile.release] lto = "off" diff --git a/bacon.toml b/bacon.toml deleted file mode 100644 index bddc645..0000000 --- a/bacon.toml +++ /dev/null @@ -1,83 +0,0 @@ -# This is a configuration file for the bacon tool -# -# Bacon repository: https://github.com/Canop/bacon -# Complete help on configuration: https://dystroy.org/bacon/config/ -# You can also check bacon's own bacon.toml file -# as an example: https://github.com/Canop/bacon/blob/main/bacon.toml - -default_job = "check" - -[jobs.check] -command = ["cargo", "check", "--color", "always", "--features", "arch"] -need_stdout = false - -[jobs.check-all] -command = [ - "cargo", - "check", - "--all-targets", - "--color", - "always", - "--features", - "arch", -] -need_stdout = false - -[jobs.clippy] -command = [ - "cargo", - "clippy", - "--all-targets", - "--color", - "always", - "--features", - "arch", -] -need_stdout = false - -[jobs.test] -command = [ - "cargo", - "test", - "--color", - "always", - "--package", - "pacdef", - "--", - "--color", - "always", # see https://github.com/Canop/bacon/issues/124 -] -need_stdout = true - -[jobs.doc] -command = ["cargo", "doc", "--color", "always", "--no-deps"] -need_stdout = false - -# If the doc compiles, then it opens in your browser and bacon switches -# to the previous job -[jobs.doc-open] -command = ["cargo", "doc", "--color", "always", "--no-deps", "--open"] -need_stdout = false -on_success = "back" # so that we don't open the browser at each change - -# You can run your application and have the result displayed in bacon, -# *if* it makes sense for this crate. You can run an example the same -# way. Don't forget the `--color always` part or the errors won't be -# properly parsed. -[jobs.run] -command = [ - "cargo", - "run", - "--color", - "always", - # put launch parameters for your program behind a `--` separator -] -need_stdout = true -allow_warnings = true - -# You may define here keybindings that would be specific to -# a project, for example a shortcut to launch a specific job. -# Shortcuts to internal functions (scrolling, toggling, etc.) -# should go in your personal global prefs.toml file instead. -[keybindings] -# alt-m = "job:my-job" diff --git a/crates/pacdef/Cargo.toml b/crates/pacdef/Cargo.toml index 8f626ca..8d331fb 100644 --- a/crates/pacdef/Cargo.toml +++ b/crates/pacdef/Cargo.toml @@ -19,14 +19,13 @@ regex = { version = "1.10", default-features = false, features = ["std"] } termios = "0.3" walkdir = "2.5" libc = "0.2" +enum_dispatch = "0.3" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" serde_yaml = "0.9" -pacdef_macros.workspace = true - # backends alpm = { version = "3.0", optional = true } rust-apt = { version = "0.7", optional = true } diff --git a/crates/pacdef/src/args/mod.rs b/crates/pacdef/src/args/mod.rs index a1e2848..c57e2ee 100644 --- a/crates/pacdef/src/args/mod.rs +++ b/crates/pacdef/src/args/mod.rs @@ -1,11 +1,11 @@ +use self::datastructure::Arguments; + mod cli; -mod datastructure; +pub mod datastructure; mod parsing; #[cfg(test)] mod tests; -pub use datastructure::*; - /// Get and parse the CLI arguments. #[must_use] pub fn get() -> Arguments { diff --git a/crates/pacdef/src/args/parsing.rs b/crates/pacdef/src/args/parsing.rs index 083cf3f..db3601d 100644 --- a/crates/pacdef/src/args/parsing.rs +++ b/crates/pacdef/src/args/parsing.rs @@ -1,4 +1,6 @@ -use super::datastructure::*; +use super::datastructure::{ + Arguments, Edit, Force, GroupAction, Groups, Noconfirm, OutputDir, PackageAction, Regex, +}; const ARGS_CONSISTENT: &str = "argument declaration and parsing must be consistent"; @@ -13,30 +15,28 @@ pub(super) fn parse(args: clap::ArgMatches) -> Arguments { } fn parse_group_args(args: &clap::ArgMatches) -> GroupAction { - use GroupAction::*; - match args.subcommand() { - Some(("edit", args)) => Edit(get_groups(args)), - Some(("export", args)) => Export(get_groups(args), get_output_dir(args), get_force(args)), - Some(("import", args)) => Import(get_groups(args)), - Some(("list", _)) => List, - Some(("new", args)) => New(get_groups(args), get_edit(args)), - Some(("remove", args)) => Remove(get_groups(args)), - Some(("show", args)) => Show(get_groups(args)), + Some(("edit", args)) => GroupAction::Edit(get_groups(args)), + Some(("export", args)) => { + GroupAction::Export(get_groups(args), get_output_dir(args), get_force(args)) + } + Some(("import", args)) => GroupAction::Import(get_groups(args)), + Some(("list", _)) => GroupAction::List, + Some(("new", args)) => GroupAction::New(get_groups(args), get_edit(args)), + Some(("remove", args)) => GroupAction::Remove(get_groups(args)), + Some(("show", args)) => GroupAction::Show(get_groups(args)), Some(value) => panic!("group subcommand was not matched: {value:?}"), None => unreachable!("prevented by clap"), } } fn parse_package_args(args: &clap::ArgMatches) -> PackageAction { - use PackageAction::*; - match args.subcommand() { - Some(("clean", args)) => Clean(get_noconfirm(args)), - Some(("review", _)) => Review, - Some(("search", args)) => Search(get_regex(args)), - Some(("sync", args)) => Sync(get_noconfirm(args)), - Some(("unmanaged", _)) => Unmanaged, + Some(("clean", args)) => PackageAction::Clean(get_noconfirm(args)), + Some(("review", _)) => PackageAction::Review, + Some(("search", args)) => PackageAction::Search(get_regex(args)), + Some(("sync", args)) => PackageAction::Sync(get_noconfirm(args)), + Some(("unmanaged", _)) => PackageAction::Unmanaged, Some(value) => panic!("package subcommand was not matched: {value:?}"), None => unreachable!("prevented by clap"), } diff --git a/crates/pacdef/src/backend/actual/arch.rs b/crates/pacdef/src/backend/actual/arch.rs index 50a56f2..e853841 100644 --- a/crates/pacdef/src/backend/actual/arch.rs +++ b/crates/pacdef/src/backend/actual/arch.rs @@ -12,9 +12,23 @@ use crate::{Group, Package}; #[derive(Debug, Clone)] pub struct Arch { - pub(crate) binary: String, - pub(crate) aur_rm_args: Vec, - pub(crate) packages: HashSet, + pub binary: String, + pub aur_rm_args: Vec, + pub packages: HashSet, +} +impl Arch { + pub fn new() -> Self { + Self { + binary: BINARY.to_string(), + aur_rm_args: vec![], + packages: HashSet::new(), + } + } +} +impl Default for Arch { + fn default() -> Self { + Self::new() + } } const BINARY: Text = "pacman"; @@ -117,13 +131,3 @@ fn convert_to_pacdef_packages(packages: HashSet) -> HashSet { fn get_db_handle() -> Result { Alpm::new("/", "/var/lib/pacman").context("connecting to DB using expected default values") } - -impl Arch { - pub(crate) fn new() -> Self { - Self { - binary: BINARY.to_string(), - aur_rm_args: vec![], - packages: HashSet::new(), - } - } -} diff --git a/crates/pacdef/src/backend/actual/debian.rs b/crates/pacdef/src/backend/actual/debian.rs index 6157a56..1daca15 100644 --- a/crates/pacdef/src/backend/actual/debian.rs +++ b/crates/pacdef/src/backend/actual/debian.rs @@ -12,7 +12,19 @@ use crate::{Group, Package}; #[derive(Debug, Clone)] pub struct Debian { - pub(crate) packages: HashSet, + pub packages: HashSet, +} +impl Debian { + pub fn new() -> Self { + Self { + packages: HashSet::new(), + } + } +} +impl Default for Debian { + fn default() -> Self { + Self::new() + } } const BINARY: Text = "apt"; @@ -94,11 +106,3 @@ impl Backend for Debian { run_external_command(cmd) } } - -impl Debian { - pub(crate) fn new() -> Self { - Self { - packages: HashSet::new(), - } - } -} diff --git a/crates/pacdef/src/backend/actual/fedora.rs b/crates/pacdef/src/backend/actual/fedora.rs index 2a7adf1..8fc7522 100644 --- a/crates/pacdef/src/backend/actual/fedora.rs +++ b/crates/pacdef/src/backend/actual/fedora.rs @@ -10,7 +10,19 @@ use crate::{Group, Package}; #[derive(Debug, Clone)] pub struct Fedora { - pub(crate) packages: HashSet, + pub packages: HashSet, +} +impl Fedora { + pub fn new() -> Self { + Self { + packages: HashSet::new(), + } + } +} +impl Default for Fedora { + fn default() -> Self { + Self::new() + } } const BINARY: Text = "dnf"; @@ -123,14 +135,6 @@ impl Backend for Fedora { } } -impl Fedora { - pub fn new() -> Self { - Self { - packages: HashSet::new(), - } - } -} - fn create_package(package: &str) -> Package { if DEFAULT_REPOS.iter().any(|repo| package.contains(repo)) && !package.contains("copr") { let package = package.split('/').nth(1).expect("Cannot be empty!"); diff --git a/crates/pacdef/src/backend/actual/flatpak.rs b/crates/pacdef/src/backend/actual/flatpak.rs index 5ed8344..6cd3ed7 100644 --- a/crates/pacdef/src/backend/actual/flatpak.rs +++ b/crates/pacdef/src/backend/actual/flatpak.rs @@ -10,8 +10,46 @@ use crate::{Group, Package}; #[derive(Debug, Clone)] pub struct Flatpak { - pub(crate) packages: HashSet, - pub(crate) systemwide: bool, + pub packages: HashSet, + pub systemwide: bool, +} +impl Flatpak { + pub fn new() -> Self { + Self { + packages: HashSet::new(), + systemwide: true, + } + } + + fn get_switches_runtime(&self) -> Switches { + if self.systemwide { + &[] + } else { + &["--user"] + } + } + + fn get_installed_packages(&self, include_implicit: bool) -> Result> { + let mut cmd = Command::new(BINARY); + cmd.args(["list", "--columns=application"]); + if !include_implicit { + cmd.arg("--app"); + } + if !self.systemwide { + cmd.arg("--user"); + } + + let output = String::from_utf8(cmd.output()?.stdout)?; + Ok(output + .lines() + .map(Package::from) + .collect::>()) + } +} +impl Default for Flatpak { + fn default() -> Self { + Self::new() + } } const BINARY: Text = "flatpak"; @@ -84,37 +122,3 @@ impl Backend for Flatpak { run_external_command(cmd) } } - -impl Flatpak { - pub(crate) fn new() -> Self { - Self { - packages: HashSet::new(), - systemwide: true, - } - } - - fn get_switches_runtime(&self) -> Switches { - if self.systemwide { - &[] - } else { - &["--user"] - } - } - - fn get_installed_packages(&self, include_implicit: bool) -> Result> { - let mut cmd = Command::new(BINARY); - cmd.args(["list", "--columns=application"]); - if !include_implicit { - cmd.arg("--app"); - } - if !self.systemwide { - cmd.arg("--user"); - } - - let output = String::from_utf8(cmd.output()?.stdout)?; - Ok(output - .lines() - .map(Package::from) - .collect::>()) - } -} diff --git a/crates/pacdef/src/backend/actual/python.rs b/crates/pacdef/src/backend/actual/python.rs index 5374951..1bfbdb8 100644 --- a/crates/pacdef/src/backend/actual/python.rs +++ b/crates/pacdef/src/backend/actual/python.rs @@ -9,10 +9,52 @@ use crate::backend::backend_trait::{Backend, Switches, Text}; use crate::backend::macros::impl_backend_constants; use crate::{Group, Package}; +macro_rules! ERROR{ + ($bin:expr) => { + panic!("Cannot use {} for package management in python. Please use a valid package manager like pip or pipx.", $bin) + }; +} + #[derive(Debug, Clone)] pub struct Python { - pub(crate) binary: String, - pub(crate) packages: HashSet, + pub binary: String, + pub packages: HashSet, +} +impl Python { + pub fn new() -> Self { + Self { + binary: BINARY.to_string(), + packages: HashSet::new(), + } + } + + fn get_switches_runtime(&self) -> Switches { + match self.get_binary() { + "pip" => &["list", "--format", "json", "--not-required", "--user"], + "pipx" => &["list", "--json"], + _ => ERROR!(self.get_binary()), + } + } + fn get_switches_explicit(&self) -> Switches { + match self.get_binary() { + "pip" => &["list", "--format", "json", "--user"], + "pipx" => &["list", "--json"], + _ => ERROR!(self.get_binary()), + } + } + + fn extract_packages(&self, output: Value) -> Result> { + match self.get_binary() { + "pip" => extract_pacdef_packages(output), + "pipx" => extract_pacdef_packages_pipx(output), + _ => ERROR!(self.get_binary()), + } + } +} +impl Default for Python { + fn default() -> Self { + Self::new() + } } const BINARY: Text = "pip"; @@ -26,12 +68,6 @@ const SWITCHES_REMOVE: Switches = &["uninstall"]; const SUPPORTS_AS_DEPENDENCY: bool = false; -macro_rules! ERROR{ - ($bin:expr) => { - panic!("Cannot use {} for package management in python. Please use a valid package manager like pip or pipx.", $bin) - }; -} - impl Backend for Python { impl_backend_constants!(); @@ -64,38 +100,6 @@ fn run_pip_command(cmd: &mut Command, args: &[&str]) -> Result { Ok(val) } -impl Python { - pub(crate) fn new() -> Self { - Self { - binary: BINARY.to_string(), - packages: HashSet::new(), - } - } - - fn get_switches_runtime(&self) -> Switches { - match self.get_binary() { - "pip" => &["list", "--format", "json", "--not-required", "--user"], - "pipx" => &["list", "--json"], - _ => ERROR!(self.get_binary()), - } - } - fn get_switches_explicit(&self) -> Switches { - match self.get_binary() { - "pip" => &["list", "--format", "json", "--user"], - "pipx" => &["list", "--json"], - _ => ERROR!(self.get_binary()), - } - } - - fn extract_packages(&self, output: Value) -> Result> { - match self.get_binary() { - "pip" => extract_pacdef_packages(output), - "pipx" => extract_pacdef_packages_pipx(output), - _ => ERROR!(self.get_binary()), - } - } -} - fn extract_pacdef_packages(value: Value) -> Result> { let result = value .as_array() diff --git a/crates/pacdef/src/backend/actual/rust.rs b/crates/pacdef/src/backend/actual/rust.rs index 76ddd05..b109814 100644 --- a/crates/pacdef/src/backend/actual/rust.rs +++ b/crates/pacdef/src/backend/actual/rust.rs @@ -12,7 +12,19 @@ use crate::{Group, Package}; #[derive(Debug, Clone)] pub struct Rust { - pub(crate) packages: HashSet, + pub packages: HashSet, +} +impl Rust { + pub fn new() -> Self { + Self { + packages: HashSet::new(), + } + } +} +impl Default for Rust { + fn default() -> Self { + Self::new() + } } const BINARY: Text = "cargo"; @@ -76,14 +88,6 @@ fn extract_packages(json: &Value) -> Result> { Ok(result) } -impl Rust { - pub(crate) fn new() -> Self { - Self { - packages: HashSet::new(), - } - } -} - fn get_crates_file() -> Result { let mut result = crate::path::get_cargo_home().context("getting cargo home dir")?; result.push(".crates2.json"); diff --git a/crates/pacdef/src/backend/actual/rustup/mod.rs b/crates/pacdef/src/backend/actual/rustup/mod.rs index 539f37e..275f85e 100644 --- a/crates/pacdef/src/backend/actual/rustup/mod.rs +++ b/crates/pacdef/src/backend/actual/rustup/mod.rs @@ -12,9 +12,25 @@ use std::process::Command; use self::helpers::{ group_components_by_toolchains, install_components, toolchain_of_component_was_already_removed, }; -pub use self::types::Rustup; use self::types::{Repotype, RustupPackage}; +#[derive(Debug, Clone)] +pub struct Rustup { + pub packages: HashSet, +} +impl Rustup { + pub fn new() -> Self { + Self { + packages: HashSet::new(), + } + } +} +impl Default for Rustup { + fn default() -> Self { + Self::new() + } +} + const BINARY: Text = "rustup"; const SECTION: Text = "rustup"; diff --git a/crates/pacdef/src/backend/actual/rustup/types.rs b/crates/pacdef/src/backend/actual/rustup/types.rs index 17ecdee..b0a8433 100644 --- a/crates/pacdef/src/backend/actual/rustup/types.rs +++ b/crates/pacdef/src/backend/actual/rustup/types.rs @@ -1,13 +1,7 @@ use anyhow::{bail, Context, Result}; -use std::collections::HashSet; use crate::{backend::backend_trait::Switches, Package}; -#[derive(Debug, Clone)] -pub struct Rustup { - pub(crate) packages: HashSet, -} - #[derive(Debug)] pub enum Repotype { Toolchain, @@ -28,14 +22,6 @@ pub struct RustupPackage { pub component: Option, } -impl Rustup { - pub fn new() -> Self { - Self { - packages: HashSet::new(), - } - } -} - impl Repotype { fn try_from(value: T) -> Result where diff --git a/crates/pacdef/src/backend/actual/void.rs b/crates/pacdef/src/backend/actual/void.rs index 03f9c78..3b4b141 100644 --- a/crates/pacdef/src/backend/actual/void.rs +++ b/crates/pacdef/src/backend/actual/void.rs @@ -12,7 +12,19 @@ use crate::{Group, Package}; #[derive(Debug, Clone)] pub struct Void { - pub(crate) packages: HashSet, + pub packages: HashSet, +} +impl Void { + pub fn new() -> Self { + Self { + packages: HashSet::new(), + } + } +} +impl Default for Void { + fn default() -> Self { + Self::new() + } } const BINARY: Text = "xbps-install"; @@ -124,11 +136,3 @@ impl Backend for Void { run_external_command(cmd) } } - -impl Void { - pub(crate) fn new() -> Self { - Self { - packages: HashSet::new(), - } - } -} diff --git a/crates/pacdef/src/backend/backend_trait.rs b/crates/pacdef/src/backend/backend_trait.rs index 5963725..11c7ecf 100644 --- a/crates/pacdef/src/backend/backend_trait.rs +++ b/crates/pacdef/src/backend/backend_trait.rs @@ -1,7 +1,5 @@ -use std::any::Any; use std::cmp::{Eq, Ord}; use std::collections::{HashMap, HashSet}; -use std::fmt::Debug; use std::hash::Hash; use std::process::Command; use std::rc::Rc; @@ -11,11 +9,12 @@ use anyhow::{Context, Result}; use crate::cmd::run_external_command; use crate::{Group, Package}; -pub(in crate::backend) type Switches = &'static [&'static str]; -pub(in crate::backend) type Text = &'static str; +pub type Switches = &'static [&'static str]; +pub type Text = &'static str; /// The trait of a struct that is used as a backend. -pub trait Backend: Debug { +#[enum_dispatch::enum_dispatch] +pub trait Backend { /// Return the actual binary. Iff the backend supports different /// binaries, you will need to overwrite this implementation to return /// the binary that was loaded at runtime. See @@ -47,7 +46,7 @@ pub trait Backend: Debug { /// Get CLI switches for the package manager to mark packages as /// dependency. This is not supported by all package managers. See - /// [`Backend::supports_as_dependency`]. + /// [`Backend::supports_as_dependency()`]. fn get_switches_make_dependency(&self) -> Switches; /// Load all packages from a set of groups. The backend will visit all groups, @@ -174,9 +173,6 @@ pub trait Backend: Debug { Ok(diff) } - /// Return a mutable reference to self as `Any`. Required for downcasting. - fn as_any_mut(&mut self) -> &mut dyn Any; - /// Whether the underlying package manager supports dependency packages. fn supports_as_dependency(&self) -> bool; } diff --git a/crates/pacdef/src/backend/iter.rs b/crates/pacdef/src/backend/iter.rs deleted file mode 100644 index 912e4c2..0000000 --- a/crates/pacdef/src/backend/iter.rs +++ /dev/null @@ -1,21 +0,0 @@ -use super::{Backend, Backends}; - -#[derive(Debug)] -pub struct BackendIter { - pub(crate) next: Option, -} - -impl Iterator for BackendIter { - type Item = Box; - - fn next(&mut self) -> Option { - match &self.next { - None => None, - Some(b) => { - let result = b.get_backend(); - self.next = b.next(); - Some(result) - } - } - } -} diff --git a/crates/pacdef/src/backend/macros.rs b/crates/pacdef/src/backend/macros.rs index c2bf102..bc1ec1b 100644 --- a/crates/pacdef/src/backend/macros.rs +++ b/crates/pacdef/src/backend/macros.rs @@ -47,10 +47,6 @@ macro_rules! impl_backend_constants { }) } - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - fn supports_as_dependency(&self) -> bool { SUPPORTS_AS_DEPENDENCY } diff --git a/crates/pacdef/src/backend/mod.rs b/crates/pacdef/src/backend/mod.rs index f581785..09e8c12 100644 --- a/crates/pacdef/src/backend/mod.rs +++ b/crates/pacdef/src/backend/mod.rs @@ -1,26 +1,52 @@ -mod actual; -mod backend_trait; -mod iter; -mod macros; +pub mod actual; +pub mod backend_trait; +pub mod macros; mod root; -mod todo_per_backend; +pub mod todo_per_backend; -pub use backend_trait::Backend; -pub use iter::BackendIter; -pub use todo_per_backend::ToDoPerBackend; +use crate::backend::backend_trait::Switches; +use crate::backend::backend_trait::Text; +use crate::Group; +use crate::Package; +use anyhow::Result; +use backend_trait::Backend; +use std::collections::HashSet; +use std::rc::Rc; -use pacdef_macros::Register; +use self::actual::{ + fedora::Fedora, flatpak::Flatpak, python::Python, rust::Rust, rustup::Rustup, void::Void, +}; -#[derive(Debug, Register)] -pub enum Backends { +#[derive(Debug)] +#[enum_dispatch::enum_dispatch(Backend)] +pub enum AnyBackend { #[cfg(feature = "arch")] - Arch, + Arch(actual::arch::Arch), #[cfg(feature = "debian")] - Debian, - Flatpak, - Fedora, - Python, - Rust, - Rustup, - Void, + Debian(actual::debian::Debian), + Flatpak(Flatpak), + Fedora(Fedora), + Python(Python), + Rust(Rust), + Rustup(Rustup), + Void(Void), +} + +impl AnyBackend { + /// Returns an iterator of every variant of backend. + pub fn iter() -> impl Iterator { + vec![ + #[cfg(feature = "arch")] + Self::Arch(actual::arch::Arch::new()), + #[cfg(feature = "debian")] + Self::Debian(actual::debian::Debian::new()), + Self::Flatpak(Flatpak::new()), + Self::Fedora(Fedora::new()), + Self::Python(Python::new()), + Self::Rust(Rust::new()), + Self::Rustup(Rustup::new()), + Self::Void(Void::new()), + ] + .into_iter() + } } diff --git a/crates/pacdef/src/backend/todo_per_backend.rs b/crates/pacdef/src/backend/todo_per_backend.rs index e74f840..4bce5df 100644 --- a/crates/pacdef/src/backend/todo_per_backend.rs +++ b/crates/pacdef/src/backend/todo_per_backend.rs @@ -2,63 +2,60 @@ use std::fmt::Write; use anyhow::{Context, Result}; -use super::Backend; +use super::{AnyBackend, Backend}; use crate::Package; -/// A vector of tuples containing a `dyn Backend` and a vector of unmanaged packages +/// A vector of tuples containing a Backends and a vector of unmanaged packages /// for that backend. /// /// This struct is used to store a list of unmanaged packages or missing packages /// for all backends. #[derive(Debug)] -pub struct ToDoPerBackend(Vec<(Box, Vec)>); - +pub struct ToDoPerBackend(Vec<(AnyBackend, Vec)>); impl ToDoPerBackend { - pub(crate) fn new() -> Self { + pub fn new() -> Self { Self(vec![]) } - pub(crate) fn push(&mut self, item: (Box, Vec)) { + pub fn push(&mut self, item: (AnyBackend, Vec)) { self.0.push(item); } - pub(crate) fn into_iter(self) -> impl Iterator, Vec)> { - self.0.into_iter() - } - - pub(crate) fn iter(&self) -> impl Iterator, Vec)> { + pub fn iter(&self) -> impl Iterator)> { self.0.iter() } - pub(crate) fn nothing_to_do_for_all_backends(&self) -> bool { + pub fn nothing_to_do_for_all_backends(&self) -> bool { self.0.iter().all(|(_, diff)| diff.is_empty()) } - pub(crate) fn install_missing_packages(&self, noconfirm: bool) -> Result<()> { + pub fn install_missing_packages(&self, noconfirm: bool) -> Result<()> { for (backend, packages) in &self.0 { if packages.is_empty() { continue; } - Backend::install_packages(&**backend, packages, noconfirm) + backend + .install_packages(packages, noconfirm) .with_context(|| format!("installing packages for {}", backend.get_section()))?; } Ok(()) } - pub(crate) fn remove_unmanaged_packages(&self, noconfirm: bool) -> Result<()> { + pub fn remove_unmanaged_packages(&self, noconfirm: bool) -> Result<()> { for (backend, packages) in &self.0 { if packages.is_empty() { continue; } - Backend::remove_packages(&**backend, packages, noconfirm) + backend + .remove_packages(packages, noconfirm) .with_context(|| format!("removing packages for {}", backend.get_section()))?; } Ok(()) } - pub(crate) fn show(&self) -> Result<()> { + pub fn show(&self) -> Result<()> { let mut parts = vec![]; for (backend, packages) in self.iter() { @@ -91,3 +88,18 @@ impl ToDoPerBackend { Ok(()) } } +impl Default for ToDoPerBackend { + fn default() -> Self { + Self::new() + } +} + +impl IntoIterator for ToDoPerBackend { + type Item = (AnyBackend, Vec); + + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} diff --git a/crates/pacdef/src/core.rs b/crates/pacdef/src/core.rs index 1a49262..88112b8 100644 --- a/crates/pacdef/src/core.rs +++ b/crates/pacdef/src/core.rs @@ -7,8 +7,12 @@ use std::path::{Path, PathBuf}; use anyhow::{bail, ensure, Context, Result}; use const_format::formatcp; -use crate::args::{self, PackageAction}; -use crate::backend::{Backend, Backends, ToDoPerBackend}; +use crate::args::datastructure::{ + Arguments, Edit, Force, GroupAction, Groups, Noconfirm, OutputDir, PackageAction, Regex, +}; +use crate::backend::backend_trait::Backend; +use crate::backend::todo_per_backend::ToDoPerBackend; +use crate::backend::AnyBackend; use crate::cmd::run_edit_command; use crate::env::should_print_debug_info; use crate::path::{binary_in_path, get_absolutized_file_paths, get_group_dir}; @@ -21,7 +25,7 @@ use crate::{review, Error}; /// Most data that is required during runtime of the program. pub struct Pacdef { /// The command line arguments. Is an `Option` so that we can take ownership later without cloning. - args: Option, + args: Option, /// The config of the program. config: Config, /// The hashset of all groups. @@ -32,7 +36,7 @@ impl Pacdef { /// Creates a new [`Pacdef`]. `config` should be passed from [`Config::load`], and `args` from /// [`args::get`]. #[must_use] - pub const fn new(args: args::Arguments, config: Config, groups: HashSet) -> Self { + pub const fn new(args: Arguments, config: Config, groups: HashSet) -> Self { Self { args: Some(args), config, @@ -52,47 +56,46 @@ impl Pacdef { /// # Panics /// /// This function panics if the `args` field is `None`. - #[allow(clippy::unit_arg)] pub fn run_action_from_arg(mut self) -> Result<()> { match self .args .take() .expect("if there were no args we would not get to here") { - args::Arguments::Group(group_args) => self.run_group_subcommand(&group_args), - args::Arguments::Package(package_args) => self.run_package_subcommand(&package_args), - args::Arguments::Version => Ok(self.show_version()), + Arguments::Group(group_args) => self.run_group_subcommand(&group_args), + Arguments::Package(package_args) => self.run_package_subcommand(&package_args), + Arguments::Version => { + self.show_version(); + + Ok(()) + } } } - fn run_group_subcommand(self, args: &args::GroupAction) -> Result<()> { - use args::GroupAction::*; - + fn run_group_subcommand(self, args: &GroupAction) -> Result<()> { match args { - Edit(args::Groups(groups)) => self.edit_groups(groups), - Export(args::Groups(groups), args::OutputDir(dir), args::Force(force)) => { + GroupAction::Edit(Groups(groups)) => self.edit_groups(groups), + GroupAction::Export(Groups(groups), OutputDir(dir), Force(force)) => { self.export_groups(groups, dir.as_ref(), *force) } - Import(args::Groups(groups)) => self.import_groups(groups), - List => self.show_groups(), - New(args::Groups(groups), args::Edit(edit)) => self.new_groups(groups, *edit), - Remove(args::Groups(groups)) => self.remove_groups(groups), - Show(args::Groups(groups)) => self.show_group_content(groups), + GroupAction::Import(Groups(groups)) => self.import_groups(groups), + GroupAction::List => self.show_groups(), + GroupAction::New(Groups(groups), Edit(edit)) => self.new_groups(groups, *edit), + GroupAction::Remove(Groups(groups)) => self.remove_groups(groups), + GroupAction::Show(Groups(groups)) => self.show_group_content(groups), } } fn run_package_subcommand(mut self, args: &PackageAction) -> Result<()> { - use args::PackageAction::*; - match args { - Clean(args::Noconfirm(noconfirm)) => self.clean_packages(*noconfirm), - Review => review::review(self.get_unmanaged_packages()?, self.groups), - Search(args::Regex(regex)) => { + PackageAction::Clean(Noconfirm(noconfirm)) => self.clean_packages(*noconfirm), + PackageAction::Review => review::review(self.get_unmanaged_packages()?, self.groups), + PackageAction::Search(Regex(regex)) => { self.warn_about_groups_that_arent_symlinks(); search::search_packages(regex, &self.groups) } - Sync(args::Noconfirm(noconfirm)) => self.install_packages(*noconfirm), - Unmanaged => self.show_unmanaged_packages(), + PackageAction::Sync(Noconfirm(noconfirm)) => self.install_packages(*noconfirm), + PackageAction::Unmanaged => self.show_unmanaged_packages(), } } @@ -103,7 +106,7 @@ impl Pacdef { eprintln!("WARNING: no group files found"); } - for mut backend in Backends::iter() { + for mut backend in AnyBackend::iter() { if self .config .disabled_backends @@ -116,39 +119,33 @@ impl Pacdef { continue; } - self.overwrite_values_from_config(&mut *backend); + self.overwrite_values_from_config(&mut backend); backend.load(&self.groups); match backend.get_missing_packages_sorted() { Ok(diff) => to_install.push((backend, diff)), - Err(error) => show_backend_query_error(&error, &*backend), + Err(error) => show_backend_query_error(&error, &backend), }; } Ok(to_install) } - fn overwrite_values_from_config(&mut self, backend: &mut dyn Backend) { + fn overwrite_values_from_config(&mut self, backend: &mut AnyBackend) { #[cfg(feature = "arch")] { - if let Some(arch) = backend.as_any_mut().downcast_mut::() { - arch.binary = self.config.aur_helper.clone(); - arch.aur_rm_args = self.config.aur_rm_args.clone(); + if let AnyBackend::Arch(arch) = backend { + arch.binary.clone_from(&self.config.aur_helper); + arch.aur_rm_args.clone_from(&self.config.aur_rm_args); } } - if let Some(flatpak) = backend - .as_any_mut() - .downcast_mut::() - { + if let AnyBackend::Flatpak(flatpak) = backend { flatpak.systemwide = self.config.flatpak_systemwide; } - if let Some(python) = backend - .as_any_mut() - .downcast_mut::() - { - python.binary = self.config.pip_binary.clone(); + if let AnyBackend::Python(python) = backend { + python.binary.clone_from(&self.config.pip_binary); } } @@ -204,7 +201,6 @@ impl Pacdef { Ok(()) } - #[allow(clippy::unused_self)] fn show_version(self) { println!("{}", get_name_and_version()); } @@ -237,7 +233,7 @@ impl Pacdef { let mut result = ToDoPerBackend::new(); - for mut backend in Backends::iter() { + for mut backend in AnyBackend::iter() { if self .config .disabled_backends @@ -250,12 +246,12 @@ impl Pacdef { continue; } - self.overwrite_values_from_config(&mut *backend); + self.overwrite_values_from_config(&mut backend); backend.load(&self.groups); match backend.get_unmanaged_packages_sorted() { Ok(unmanaged) => result.push((backend, unmanaged)), - Err(error) => show_backend_query_error(&error, &*backend), + Err(error) => show_backend_query_error(&error, &backend), }; } Ok(result) @@ -265,7 +261,6 @@ impl Pacdef { /// /// This methods cannot return an error. It returns a `Result` to be consistent /// with other methods. - #[allow(clippy::unnecessary_wraps)] fn show_groups(self) -> Result<()> { self.warn_about_groups_that_arent_symlinks(); @@ -354,7 +349,6 @@ impl Pacdef { Ok(()) } - #[allow(clippy::unused_self)] fn import_groups(&self, file_names: &[String]) -> Result<()> { let files = get_absolutized_file_paths(file_names)?; let groups_dir = get_group_dir()?; @@ -410,7 +404,6 @@ impl Pacdef { /// - a group with the same name already exists, /// - the editor cannot be run, or /// - if we do not have permission to write to the group dir. - #[allow(clippy::unused_self)] fn new_groups(&self, new_groups: &[String], edit: bool) -> Result<()> { let group_path = get_group_dir()?; @@ -585,8 +578,7 @@ fn find_groups_by_name<'a>(names: &[String], groups: &'a HashSet) -> Resu /// Show the error chain for an error that has occurred when a backend was queried /// if the `RUST_BACKTRACE` env variable is set to `1` or `full`. -#[allow(clippy::option_if_let_else)] -fn show_backend_query_error(error: &anyhow::Error, backend: &dyn Backend) { +fn show_backend_query_error(error: &anyhow::Error, backend: &AnyBackend) { let section = backend.get_section(); if should_print_debug_info() { eprintln!("WARNING: skipping backend '{section}':"); @@ -628,7 +620,7 @@ pub const fn get_version_string() -> &'static str { /// Get a vector with the names of all backends, sorted alphabetically. fn get_included_backends() -> Vec<&'static str> { let mut result = vec![]; - for backend in Backends::iter() { + for backend in AnyBackend::iter() { result.push(backend.get_section()); } result.sort_unstable(); diff --git a/crates/pacdef/src/grouping/group.rs b/crates/pacdef/src/grouping/group.rs index 25aab0f..328ffc0 100644 --- a/crates/pacdef/src/grouping/group.rs +++ b/crates/pacdef/src/grouping/group.rs @@ -18,13 +18,13 @@ use super::{Package, Section}; pub struct Group { /// Name of the group (file name from which it was read, relative to the group /// base dir). - pub(crate) name: String, + pub name: String, /// The sections in the file which in turn hold the packages. - pub(crate) sections: HashSet
, + pub sections: HashSet
, /// The absolute path of the original file. - pub(crate) path: PathBuf, + pub path: PathBuf, /// Whether the main program should warn this group being loaded from a symlink. - pub(crate) warn_symlink: bool, + pub warn_symlink: bool, } impl Group { @@ -177,7 +177,7 @@ impl Group { /// /// This function returns an error if the group file cannot be read, or if the /// file cannot be written to. - pub(crate) fn save_packages(&self, section_header: &str, packages: &[Package]) -> Result<()> { + pub fn save_packages(&self, section_header: &str, packages: &[Package]) -> Result<()> { let mut content = read_to_string(&self.path) .with_context(|| format!("reading existing file contents from {:?}", &self.path))?; diff --git a/crates/pacdef/src/grouping/mod.rs b/crates/pacdef/src/grouping/mod.rs index 18bd8f1..d6664ce 100644 --- a/crates/pacdef/src/grouping/mod.rs +++ b/crates/pacdef/src/grouping/mod.rs @@ -8,6 +8,7 @@ one (strictly spoken zero) or more [`Package`]s. On start-up `pacdef` will load all groups using [`Group::load`], which in turn will get all packages from all sections. */ + mod group; mod package; mod section; diff --git a/crates/pacdef/src/grouping/package.rs b/crates/pacdef/src/grouping/package.rs index efbb437..078a922 100644 --- a/crates/pacdef/src/grouping/package.rs +++ b/crates/pacdef/src/grouping/package.rs @@ -5,8 +5,10 @@ use std::hash::Hash; /// optionally a `repo`. #[derive(Debug, Eq, PartialOrd, Ord, Clone)] pub struct Package { - pub(crate) name: String, - pub(crate) repo: Option, + /// The name of the package + pub name: String, + /// Optionally, which repository the package belongs to + pub repo: Option, } fn remove_comment_and_trim_whitespace(s: &str) -> &str { @@ -51,7 +53,7 @@ impl Package { /// Try to parse a string (from a line in a group file) and return a package. /// From the string, any possible comment is removed and whitespace is trimmed. /// Returns `None` if there is nothing left after trimming. - pub(crate) fn try_from(s: S) -> Option + pub fn try_from(s: S) -> Option where S: AsRef, { @@ -116,11 +118,10 @@ mod tests { assert_eq!(repo, None); } - #[allow(clippy::unwrap_used)] #[test] fn from() { let x = "myrepo/somepackage # ".to_string(); - let p = Package::try_from(x).unwrap(); + let p = Package::try_from(x).expect("this should be a valid package line"); assert_eq!(p.name, "somepackage"); assert_eq!(p.repo, Some("myrepo".to_string())); } diff --git a/crates/pacdef/src/grouping/section.rs b/crates/pacdef/src/grouping/section.rs index 27ee76b..69bb64c 100644 --- a/crates/pacdef/src/grouping/section.rs +++ b/crates/pacdef/src/grouping/section.rs @@ -14,13 +14,11 @@ pub struct Section { } impl Section { - pub(crate) fn new(name: String, packages: HashSet) -> Self { + pub fn new(name: String, packages: HashSet) -> Self { Self { name, packages } } - pub(crate) fn try_from_lines<'a>( - iter: &mut Peekable>, - ) -> Result { + pub fn try_from_lines<'a>(iter: &mut Peekable>) -> Result { let name = find_next_section_name(iter)?; let mut packages = HashSet::new(); diff --git a/crates/pacdef/src/lib.rs b/crates/pacdef/src/lib.rs index d910590..d29022b 100644 --- a/crates/pacdef/src/lib.rs +++ b/crates/pacdef/src/lib.rs @@ -1,6 +1,4 @@ -/*! -This library contains all logic that happens in `pacdef` under the hood. -*/ +//! This library contains all logic that happens in `pacdef`. #![warn( clippy::as_conversions, @@ -23,7 +21,7 @@ This library contains all logic that happens in `pacdef` under the hood. )] mod args; -mod backend; +pub(crate) mod backend; mod cmd; mod config; mod core; @@ -40,6 +38,4 @@ pub use crate::config::Config; pub use crate::core::Pacdef; pub use crate::errors::Error; pub use crate::grouping::Group; -pub(crate) use crate::grouping::Package; - -extern crate pacdef_macros; +pub use crate::grouping::Package; diff --git a/crates/pacdef/src/main.rs b/crates/pacdef/src/main.rs index 046a50b..a8619c4 100644 --- a/crates/pacdef/src/main.rs +++ b/crates/pacdef/src/main.rs @@ -1,6 +1,4 @@ -/*! -Main program for `pacdef`. -*/ +//! Main program for `pacdef`. #![warn( clippy::as_conversions, diff --git a/crates/pacdef/src/path.rs b/crates/pacdef/src/path.rs index 03bcada..9c47fb7 100644 --- a/crates/pacdef/src/path.rs +++ b/crates/pacdef/src/path.rs @@ -28,7 +28,7 @@ pub fn get_group_dir() -> Result { /// # Errors /// /// This function will return an error if `$XDG_CONFIG_HOME` cannot be determined. -pub(crate) fn get_pacdef_base_dir() -> Result { +pub fn get_pacdef_base_dir() -> Result { let mut dir = get_xdg_config_home().context("getting XDG_CONFIG_HOME")?; dir.push("pacdef"); Ok(dir) @@ -40,7 +40,7 @@ pub(crate) fn get_pacdef_base_dir() -> Result { /// /// This function will return an error if neither the `$CARGO_HOME` nor /// the `$HOME` environment variables are set. -pub(crate) fn get_cargo_home() -> Result { +pub fn get_cargo_home() -> Result { if let Ok(config) = env::var("CARGO_HOME") { Ok(config.into()) } else { @@ -72,7 +72,7 @@ fn get_xdg_config_home() -> Result { /// # Errors /// /// This function will return an error if the `$HOME` variable is not set. -pub(crate) fn get_home_dir() -> Result { +pub fn get_home_dir() -> Result { Ok(env::var("HOME").context("getting $HOME variable")?.into()) } @@ -106,7 +106,7 @@ pub fn get_config_path_old_version() -> Result { /// # Errors /// /// This function returns an error if `$PATH` is not set. -pub(crate) fn binary_in_path(name: &str) -> Result { +pub fn binary_in_path(name: &str) -> Result { let paths = env::var_os("PATH").context("getting $PATH")?; for dir in env::split_paths(&paths) { let full_path = dir.join(name); @@ -123,7 +123,7 @@ pub(crate) fn binary_in_path(name: &str) -> Result { /// /// Panics if at least one element in `base_path` does not match the corresponding /// element in `full_path`. -pub(crate) fn get_relative_path

(full_path: P, base_path: P) -> PathBuf +pub fn get_relative_path

(full_path: P, base_path: P) -> PathBuf where P: AsRef, { @@ -138,7 +138,11 @@ where } /// For each file argument, return the absolute path to the file. -pub(crate) fn get_absolutized_file_paths(arg_match: &[String]) -> Result> { +/// +/// # Errors +/// +/// Returns an error if any of the files cannot be absolutized. +pub fn get_absolutized_file_paths(arg_match: &[String]) -> Result> { let mut result = vec![]; for item in arg_match { diff --git a/crates/pacdef/src/review/datastructures.rs b/crates/pacdef/src/review/datastructures.rs index 69493c3..8dba080 100644 --- a/crates/pacdef/src/review/datastructures.rs +++ b/crates/pacdef/src/review/datastructures.rs @@ -1,7 +1,6 @@ use std::rc::Rc; -use crate::backend::Backend; -use crate::{Group, Package}; +use crate::{backend::AnyBackend, Group, Package}; use super::strategy::Strategy; @@ -26,7 +25,7 @@ pub(super) enum ReviewIntention { #[derive(Debug)] pub(super) struct ReviewsPerBackend { - items: Vec<(Box, Vec)>, + items: Vec<(AnyBackend, Vec)>, } impl ReviewsPerBackend { @@ -38,7 +37,7 @@ impl ReviewsPerBackend { self.items.iter().all(|(_, vec)| vec.is_empty()) } - pub(super) fn push(&mut self, value: (Box, Vec)) { + pub(super) fn push(&mut self, value: (AnyBackend, Vec)) { self.items.push(value); } @@ -77,9 +76,9 @@ impl ReviewsPerBackend { } impl IntoIterator for ReviewsPerBackend { - type Item = (Box, Vec); + type Item = (AnyBackend, Vec); - type IntoIter = std::vec::IntoIter<(Box, Vec)>; + type IntoIter = std::vec::IntoIter<(AnyBackend, Vec)>; fn into_iter(self) -> Self::IntoIter { self.items.into_iter() diff --git a/crates/pacdef/src/review/mod.rs b/crates/pacdef/src/review/mod.rs index 837df00..1944826 100644 --- a/crates/pacdef/src/review/mod.rs +++ b/crates/pacdef/src/review/mod.rs @@ -6,7 +6,8 @@ use std::rc::Rc; use anyhow::Result; -use crate::backend::{Backend, ToDoPerBackend}; +use crate::backend::backend_trait::Backend; +use crate::backend::todo_per_backend::ToDoPerBackend; use crate::ui::{get_user_confirmation, read_single_char_from_terminal}; use crate::{Group, Package}; @@ -27,11 +28,11 @@ pub fn review( return Ok(()); } - 'outer: for (backend, packages) in todo_per_backend.into_iter() { + 'outer: for (backend, packages) in todo_per_backend { let mut actions = vec![]; for package in packages { println!("{}: {package}", backend.get_section()); - match get_action_for_package(package, &groups, &mut actions, &*backend)? { + match get_action_for_package(package, &groups, &mut actions, &backend)? { ContinueWithReview::Yes => continue, ContinueWithReview::No => return Ok(()), ContinueWithReview::NoAndApply => { @@ -161,9 +162,8 @@ fn print_enumerated_groups(groups: &[Rc]) { } } -#[allow(clippy::as_conversions)] // this cannot introduce errors for any reasonably sized numbers. fn get_amount_of_digits_for_number(number: usize) -> usize { - (number as f64).log10().trunc() as usize + 1 + number.to_string().len() } fn ask_group(groups: &[Rc]) -> Result>> { diff --git a/crates/pacdef/src/review/strategy.rs b/crates/pacdef/src/review/strategy.rs index 3d92b94..c9bb536 100644 --- a/crates/pacdef/src/review/strategy.rs +++ b/crates/pacdef/src/review/strategy.rs @@ -2,12 +2,14 @@ use std::rc::Rc; use anyhow::Result; -use crate::backend::Backend; -use crate::{Group, Package}; +use crate::{ + backend::{backend_trait::Backend, AnyBackend}, + Group, Package, +}; #[derive(Debug)] pub(super) struct Strategy { - backend: Box, + backend: AnyBackend, delete: Vec, as_dependency: Vec, assign_group: Vec<(Package, Rc)>, @@ -15,7 +17,7 @@ pub(super) struct Strategy { impl Strategy { pub(super) fn new( - backend: Box, + backend: AnyBackend, delete: Vec, as_dependency: Vec, assign_group: Vec<(Package, Rc)>, diff --git a/crates/pacdef/src/search.rs b/crates/pacdef/src/search.rs index 02eabb8..827ea6f 100644 --- a/crates/pacdef/src/search.rs +++ b/crates/pacdef/src/search.rs @@ -63,8 +63,8 @@ fn print_triples(mut vec: Vec<(&Group, &Section, &Package)>) { } fn save_group_and_section_name(g0: &mut String, g: &Group, s0: &mut String, s: &Section) { - *g0 = g.name.clone(); - *s0 = s.name.clone(); + g0.clone_from(&g.name); + s0.clone_from(&s.name); } fn print_separator_unless_exhausted( diff --git a/crates/pacdef_macros/Cargo.toml b/crates/pacdef_macros/Cargo.toml deleted file mode 100644 index 59002ad..0000000 --- a/crates/pacdef_macros/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "pacdef_macros" -description = "procedural macros for pacdef" -version = "1.0.1" - -edition.workspace = true -license.workspace = true -repository.workspace = true -readme.workspace = true -keywords.workspace = true -categories.workspace = true - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = "2.0" - -[lib] -proc-macro = true diff --git a/crates/pacdef_macros/src/lib.rs b/crates/pacdef_macros/src/lib.rs deleted file mode 100644 index aeff0e9..0000000 --- a/crates/pacdef_macros/src/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -/*! -Procedural macros for `pacdef`. -*/ - -#![warn( - clippy::as_conversions, - clippy::cognitive_complexity, - clippy::explicit_iter_loop, - clippy::explicit_into_iter_loop, - clippy::map_entry, - clippy::missing_errors_doc, - clippy::missing_panics_doc, - clippy::option_if_let_else, - clippy::redundant_pub_crate, - clippy::semicolon_if_nothing_returned, - clippy::unnecessary_wraps, - clippy::unused_self, - clippy::unwrap_used, - clippy::use_debug, - clippy::use_self, - clippy::wildcard_dependencies, - missing_docs -)] - -mod register; - -use proc_macro::TokenStream; - -/// Derive (1) an iterator over the variants, (2) imports for the individual backends, and (3) -/// instantiationn code for each backend. -#[proc_macro_derive(Register)] -pub fn register(input: TokenStream) -> TokenStream { - register::register(input) -} diff --git a/crates/pacdef_macros/src/register.rs b/crates/pacdef_macros/src/register.rs deleted file mode 100644 index 1545ddc..0000000 --- a/crates/pacdef_macros/src/register.rs +++ /dev/null @@ -1,111 +0,0 @@ -use proc_macro::TokenStream; -use quote::quote; -use syn::{DeriveInput, __private::TokenStream2}; - -pub fn register(input: TokenStream) -> TokenStream { - let input = syn::parse::(input).expect("I don't know when this could fail"); - - let name = &input.ident; - - let syn::Data::Enum(enum_data) = &input.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( - name: &syn::Ident, - first_variant: &syn::Ident, - variant_backend: T, - variant_next: U, - variant_imports: V, -) -> TokenStream2 -where - T: Iterator, - U: Iterator, - V: Iterator, -{ - let expanded = quote! { - #(#variant_imports)* - - impl #name { - pub fn iter() -> BackendIter { - BackendIter { - next: Some(Self::#first_variant), - } - } - fn get_backend(&self) -> Box { - match self { - #(#variant_backend)* - } - } - fn next(&self) -> Option { - match self { - #(#variant_next)* - } - } - } - }; - expanded -} - -fn generate_variant_imports(enum_data: &syn::DataEnum) -> impl Iterator + '_ { - 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 use actual::#variant_module::#variant_name; - - } - }); - variant_imports -} - -fn generate_variant_matches_next( - enum_data: &syn::DataEnum, -) -> impl Iterator + '_ { - 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 + '_ { - 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 -}