diff --git a/src/args.rs b/src/args.rs index a21df05..ace1a36 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,5 +1,6 @@ #![allow(clippy::module_name_repetitions)] +use crate::operations::SearchBy; use clap::{Parser, Subcommand, ValueHint}; #[derive(Debug, Clone, Parser)] @@ -104,7 +105,11 @@ pub struct SearchArgs { /// The string the package must match in the search #[clap(required = true)] - pub search: Vec, + pub search: String, + + /// Searches by a specific field + #[clap(long, short)] + pub by: Option, } #[derive(Default, Debug, Clone, Parser)] diff --git a/src/internal/rpc.rs b/src/internal/rpc.rs index 9e43f76..c8bc14d 100644 --- a/src/internal/rpc.rs +++ b/src/internal/rpc.rs @@ -1,4 +1,4 @@ -use aur_rpc::{PackageInfo, PackageMetadata}; +use aur_rpc::{PackageInfo, PackageMetadata, SearchField}; use super::error::AppResult; pub const URL: &str = "https://aur.archlinux.org/"; @@ -9,8 +9,15 @@ pub async fn rpcinfo(pkg: &str) -> AppResult> { Ok(packages.into_iter().next()) } -pub async fn rpcsearch(pkg: String) -> AppResult> { - let search_results = aur_rpc::search(pkg).await?; +pub async fn rpcsearch( + query: String, + by_field: Option, +) -> AppResult> { + let search_results = if let Some(field) = by_field { + aur_rpc::search_by(field, query).await? + } else { + aur_rpc::search(query).await? + }; Ok(search_results) } diff --git a/src/main.rs b/src/main.rs index 67162d5..33c097c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -126,10 +126,11 @@ async fn cmd_remove(args: RemoveArgs, options: Options) { #[tracing::instrument(level = "trace")] async fn cmd_search(args: SearchArgs, options: Options) { - let query_string = args.search.join(" "); + let query_string = args.search; + if args.aur { info!("Searching AUR for {}", &query_string); - operations::aur_search(&query_string, options).await; + operations::aur_search(&query_string, args.by, options).await; } if args.repo { info!("Searching repos for {}", &query_string); @@ -139,7 +140,7 @@ async fn cmd_search(args: SearchArgs, options: Options) { if !args.aur && !args.repo { info!("Searching AUR and repos for {}", &query_string); operations::search(&query_string, options).await; - operations::aur_search(&query_string, options).await; + operations::aur_search(&query_string, args.by, options).await; } } diff --git a/src/operations/mod.rs b/src/operations/mod.rs index ef348bb..0843999 100644 --- a/src/operations/mod.rs +++ b/src/operations/mod.rs @@ -1,7 +1,7 @@ pub use aur_install::*; pub use clean::*; pub use install::*; -pub use search::{aur_search, repo_search as search}; +pub use search::{aur_search, repo_search as search, SearchBy}; pub use uninstall::*; pub use upgrade::*; diff --git a/src/operations/search.rs b/src/operations/search.rs index 55a5c69..e51ad45 100644 --- a/src/operations/search.rs +++ b/src/operations/search.rs @@ -1,13 +1,16 @@ +use std::str::FromStr; + use crate::internal::commands::ShellCommand; use crate::internal::error::SilentUnwrap; use crate::internal::exit_code::AppExitCode; use crate::internal::rpc::rpcsearch; use crate::{log, Options}; +use aur_rpc::SearchField; #[tracing::instrument(level = "trace")] -pub async fn aur_search(query: &str, options: Options) { +pub async fn aur_search(query: &str, by_field: Option, options: Options) { let verbosity = options.verbosity; - let packages = rpcsearch(query.to_string()) + let packages = rpcsearch(query.to_string(), by_field.map(SearchBy::into)) .await .silent_unwrap(AppExitCode::RpcError); let total_results = packages.len(); @@ -45,3 +48,56 @@ pub async fn repo_search(query: &str, options: Options) { println!("{}", output) } + +/// Represents a field to search by +#[derive(Debug, Clone, Copy)] +pub enum SearchBy { + /// Searches by name + Name, + /// Searches name and description + NameDesc, + /// Searches by package maintainer + Maintainer, + /// Searches for packages that depend on the given keywods + Depends, + /// Searches for packages that require the given keywords to be build + MakeDepends, + /// Searches for packages that optionally depend on the given keywods + OptDepends, + /// Searches for packages that require the given keywods to be present + CheckDepends, +} + +impl FromStr for SearchBy { + type Err = String; + + fn from_str(s: &str) -> Result { + let arg = match s { + "name" => Self::Name, + "name-desc" => Self::NameDesc, + "maintainer" => Self::Maintainer, + "depends" => Self::Depends, + "makedepends" | "make-depends" => Self::MakeDepends, + "optdepends" | "opt-depends" => Self::OptDepends, + "checkdepends" | "check-depends" => Self::CheckDepends, + directive => return Err(format!("Invalid search by directive '{directive}'")), + }; + + Ok(arg) + } +} + +#[allow(clippy::from_over_into)] +impl Into for SearchBy { + fn into(self) -> SearchField { + match self { + SearchBy::Name => SearchField::Name, + SearchBy::NameDesc => SearchField::NameDesc, + SearchBy::Maintainer => SearchField::Maintainer, + SearchBy::Depends => SearchField::Depends, + SearchBy::MakeDepends => SearchField::MakeDepends, + SearchBy::OptDepends => SearchField::OptDepends, + SearchBy::CheckDepends => SearchField::CheckDepends, + } + } +}