Initial implementation of sorting aur and repo results together

i18n
Michal 2 years ago committed by GitHub
parent e3d3135e43
commit b484e2b268
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

1
Cargo.lock generated

@ -18,6 +18,7 @@ dependencies = [
"rm_rf", "rm_rf",
"serde", "serde",
"spinoff", "spinoff",
"strsim",
"textwrap", "textwrap",
"toml", "toml",
"ureq", "ureq",

@ -35,4 +35,5 @@ spinoff = { version = "0.5.2", default-features = false }
textwrap = { version = "0.15.0", features = [ "terminal_size", "smawk" ] } textwrap = { version = "0.15.0", features = [ "terminal_size", "smawk" ] }
chrono = { version = "0.4.22", default-features = false, features = [ "clock", "std", "wasmbind" ] } chrono = { version = "0.4.22", default-features = false, features = [ "clock", "std", "wasmbind" ] }
toml = { version = "0.5.9", default-features = false } toml = { version = "0.5.9", default-features = false }
crossterm = { version = "0.25.0", default-features = false } crossterm = { version = "0.25.0", default-features = false }
strsim = { version = "0.10.0", default-features = false }

@ -1,6 +1,5 @@
#![allow(clippy::module_name_repetitions)] #![allow(clippy::module_name_repetitions)]
use crate::operations::SearchBy;
use clap::{Parser, Subcommand, ValueHint}; use clap::{Parser, Subcommand, ValueHint};
#[derive(Debug, Clone, Parser)] #[derive(Debug, Clone, Parser)]
@ -105,11 +104,7 @@ pub struct SearchArgs {
/// The string the package must match in the search /// The string the package must match in the search
#[clap(required = true)] #[clap(required = true)]
pub search: Vec<String>, pub search: String,
/// Sets the search-by directive for searching the AUR only
#[clap(long, short, possible_values = SearchBy::variants())]
pub by: Option<SearchBy>,
} }
#[derive(Default, Debug, Clone, Parser)] #[derive(Default, Debug, Clone, Parser)]

@ -1,4 +1,3 @@
use crate::operations::SearchBy;
use std::sync::Arc; use std::sync::Arc;
#[derive(serde::Deserialize, Debug, Clone)] #[derive(serde::Deserialize, Debug, Clone)]
@ -76,7 +75,7 @@ pub fn rpcinfo(pkg: &str) -> InfoResults {
} }
/// Return a struct of type [`SearchResults`] from the AUR. /// Return a struct of type [`SearchResults`] from the AUR.
pub fn rpcsearch(pkg: &str, by: SearchBy) -> SearchResults { pub fn rpcsearch(pkg: &str) -> SearchResults {
// Initialise TLS connector // Initialise TLS connector
let tls_connector = Arc::new(native_tls::TlsConnector::new().unwrap()); let tls_connector = Arc::new(native_tls::TlsConnector::new().unwrap());
@ -88,8 +87,8 @@ pub fn rpcsearch(pkg: &str, by: SearchBy) -> SearchResults {
// Send request and parse results into json // Send request and parse results into json
agent agent
.get(&format!( .get(&format!(
"https://aur.archlinux.org/rpc/?v=5&type=search&by={}&arg={}", "https://aur.archlinux.org/rpc/?v=5&type=search&arg={}",
by, pkg pkg
)) ))
.call() .call()
.unwrap() .unwrap()

@ -4,8 +4,11 @@
use args::Args; use args::Args;
use clap::{CommandFactory, Parser}; use clap::{CommandFactory, Parser};
use clap_complete::{Generator, Shell}; use clap_complete::{Generator, Shell};
use strsim::sorensen_dice;
use internal::commands::ShellCommand; use internal::commands::ShellCommand;
use internal::error::SilentUnwrap; use internal::error::SilentUnwrap;
use std::env; use std::env;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
@ -17,6 +20,7 @@ use crate::args::{
use crate::internal::exit_code::AppExitCode; use crate::internal::exit_code::AppExitCode;
use crate::internal::utils::pager; use crate::internal::utils::pager;
use crate::internal::{detect, init, sort, start_sudoloop, structs::Options}; use crate::internal::{detect, init, sort, start_sudoloop, structs::Options};
use crate::operations::ResultsVec;
#[global_allocator] #[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
@ -186,7 +190,7 @@ fn cmd_remove(args: RemoveArgs, options: Options) {
fn cmd_search(args: &SearchArgs, options: Options) { fn cmd_search(args: &SearchArgs, options: Options) {
// Initialise variables // Initialise variables
let query_string = args.search.join(" "); let query_string = &args.search;
// Logic for searching // Logic for searching
let repo = args.repo || env::args().collect::<Vec<String>>()[1] == "-Ssr"; let repo = args.repo || env::args().collect::<Vec<String>>()[1] == "-Ssr";
@ -198,12 +202,12 @@ fn cmd_search(args: &SearchArgs, options: Options) {
let rsp = spinner!("Searching repos for {}", query_string); let rsp = spinner!("Searching repos for {}", query_string);
// Search repos // Search repos
let ret = operations::search(&query_string, options); let ret = operations::search(query_string, options);
rsp.stop_bold("Repo search complete"); rsp.stop_bold("Repo search complete");
ret ret
} else { } else {
"".to_string() Vec::new()
}; };
// Start AUR spinner // Start AUR spinner
@ -214,30 +218,46 @@ fn cmd_search(args: &SearchArgs, options: Options) {
let asp = spinner!("Searching AUR for {}", query_string); let asp = spinner!("Searching AUR for {}", query_string);
// Search AUR // Search AUR
let ret = operations::aur_search(&query_string, options, args.by.unwrap_or_default()); let ret = operations::aur_search(&query_string, options);
asp.stop_bold("AUR search complete"); asp.stop_bold("AUR search complete");
ret ret
} else { } else {
"".to_string() Vec::new()
}; };
let results = repo_results + "\n" + &aur_results; let mut results = repo_results
.into_iter()
.chain(aur_results)
.collect::<Vec<_>>();
// Sort results by how closely they match the query
results.sort_by(|a, b| {
let a_score = sorensen_dice(&a.name, query_string);
let b_score = sorensen_dice(&b.name, query_string);
b_score
.partial_cmp(&a_score)
.unwrap_or(std::cmp::Ordering::Equal)
});
let results = ResultsVec::from(results);
// Print results either way, so that the user can see the results after they exit `less` // Print results either way, so that the user can see the results after they exit `less`
let text = if internal::uwu_enabled() { let text = if internal::uwu_enabled() {
uwu!(results.trim()) uwu!(results.to_string())
} else { } else {
results.trim().to_string() results.to_string()
}; };
let text = text.trim().to_string();
println!("{}", text); println!("{}", text);
// Check if results are longer than terminal height // Check if results are longer than terminal height
if results.lines().count() > crossterm::terminal::size().unwrap().1 as usize { if results.0.len() > (crossterm::terminal::size().unwrap().1 / 2).into() {
// If so, paginate results // If so, paginate results
#[allow(clippy::let_underscore_drop)] #[allow(clippy::let_underscore_drop)]
let _ = pager(&results.trim().to_string()); let _ = pager(&results.to_string());
} }
} }

@ -1,7 +1,7 @@
pub use aur_install::*; pub use aur_install::*;
pub use clean::*; pub use clean::*;
pub use install::*; pub use install::*;
pub use search::{aur_search, repo_search as search, SearchBy}; pub use search::{aur_search, repo_search as search, ResultsVec};
pub use uninstall::*; pub use uninstall::*;
pub use upgrade::*; pub use upgrade::*;

@ -3,7 +3,6 @@ use colored::Colorize;
use textwrap::wrap; use textwrap::wrap;
use std::fmt::Display; use std::fmt::Display;
use std::str::FromStr;
use crate::internal::commands::ShellCommand; use crate::internal::commands::ShellCommand;
use crate::internal::error::SilentUnwrap; use crate::internal::error::SilentUnwrap;
@ -12,72 +11,69 @@ use crate::internal::rpc::rpcsearch;
use crate::{log, Options}; use crate::{log, Options};
#[allow(clippy::module_name_repetitions)] #[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone)]
pub enum SearchBy { pub struct SearchResult {
Name, pub repo: String,
NameDesc, pub name: String,
Maintainer, pub version: String,
Depends, pub ood: Option<usize>,
MakeDepends, pub description: String,
OptDepends,
CheckDepends,
} }
impl FromStr for SearchBy { impl Display for SearchResult {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"name" => Ok(Self::Name),
"name-desc" => Ok(Self::NameDesc),
"maintainer" => Ok(Self::Maintainer),
"depends" => Ok(Self::Depends),
"makedepends" => Ok(Self::MakeDepends),
"optdepends" => Ok(Self::OptDepends),
"checkdepends" => Ok(Self::CheckDepends),
_ => Err(format!("Invalid search-by directive \"{}\"", s)),
}
}
}
impl Display for SearchBy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 4)
Self::Name => write!(f, "name"), .subsequent_indent(" ");
Self::NameDesc => write!(f, "name-desc"), let description = wrap(&self.description, opts).join("\n");
Self::Maintainer => write!(f, "maintainer"), write!(
Self::Depends => write!(f, "depends"), f,
Self::MakeDepends => write!(f, "makedepends"), "{}{} {} {}\n {}",
Self::OptDepends => write!(f, "optdepends"), if self.repo == "aur" {
Self::CheckDepends => write!(f, "checkdepends"), (self.repo.clone() + "/").bold().cyan()
} } else {
(self.repo.clone() + "/").bold().purple()
},
self.name.bold(),
self.version.bold().green(),
if self.ood.is_some() {
format!(
"[out of date: since {}]",
Local
.timestamp(self.ood.unwrap().try_into().unwrap(), 0)
.date_naive()
)
.bold()
.red()
} else {
"".bold()
},
description
)
} }
} }
impl Default for SearchBy { pub struct ResultsVec(pub Vec<SearchResult>);
fn default() -> Self {
Self::NameDesc impl From<Vec<SearchResult>> for ResultsVec {
fn from(v: Vec<SearchResult>) -> Self {
Self(v)
} }
} }
impl SearchBy { impl Display for ResultsVec {
pub fn variants() -> Vec<&'static str> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
vec![ for result in &self.0 {
"name", writeln!(f, "{}", result)?;
"name-desc", }
"maintainer", Ok(())
"depends",
"makedepends",
"optdepends",
"checkdepends",
]
} }
} }
#[allow(clippy::module_name_repetitions)] #[allow(clippy::module_name_repetitions)]
/// Searches for packages from the AUR and returns wrapped results /// Searches for packages from the AUR and returns wrapped results
pub fn aur_search(query: &str, options: Options, by: SearchBy) -> String { pub fn aur_search(query: &str, options: Options) -> Vec<SearchResult> {
// Query AUR for package info // Query AUR for package info
let res = rpcsearch(query, by); let res = rpcsearch(query);
// Get verbosity // Get verbosity
let verbosity = options.verbosity; let verbosity = options.verbosity;
@ -85,36 +81,17 @@ pub fn aur_search(query: &str, options: Options, by: SearchBy) -> String {
// Format output // Format output
let mut results_vec = vec![]; let mut results_vec = vec![];
for package in &res.results { for package in &res.results {
// Define wrapping options let result = SearchResult {
let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 4) repo: "aur".to_string(),
.subsequent_indent(" "); name: package.name.to_string(),
version: package.version.to_string(),
let result = format!( ood: package.out_of_date,
"{}{} {} {}\n {}", description: package
"aur/".cyan().bold(), .description
package.name.bold(), .as_ref()
package.version.green().bold(), .unwrap_or(&"No description".to_string())
if package.out_of_date.is_some() { .to_string(),
format!( };
"[out of date: since {}]",
Local
.timestamp(package.out_of_date.unwrap().try_into().unwrap(), 0)
.date_naive()
)
.red()
.bold()
} else {
"".bold()
},
wrap(
package
.description
.as_ref()
.unwrap_or(&"No description".to_string()),
opts,
)
.join("\n"),
);
results_vec.push(result); results_vec.push(result);
} }
@ -126,19 +103,12 @@ pub fn aur_search(query: &str, options: Options, by: SearchBy) -> String {
); );
} }
results_vec.join("\n") results_vec
}
struct SearchResult {
repo: String,
name: String,
version: String,
description: String,
} }
#[allow(clippy::module_name_repetitions)] #[allow(clippy::module_name_repetitions)]
/// Searches for packages from the repos and returns wrapped results /// Searches for packages from the repos and returns wrapped results
pub fn repo_search(query: &str, options: Options) -> String { pub fn repo_search(query: &str, options: Options) -> Vec<SearchResult> {
// Initialise variables // Initialise variables
let verbosity = options.verbosity; let verbosity = options.verbosity;
@ -156,22 +126,19 @@ pub fn repo_search(query: &str, options: Options) -> String {
// Initialise results vector // Initialise results vector
let mut results_vec: Vec<SearchResult> = vec![]; let mut results_vec: Vec<SearchResult> = vec![];
let clone = lines.clone().collect::<Vec<&str>>();
if clone.len() == 1 && clone[0].is_empty() {
// If no results, return empty string
return "".to_string();
}
// Iterate over lines // Iterate over lines
for line in lines { for line in lines {
let parts: Vec<&str> = line.split('\\').collect(); if line.contains('\\') {
let res = SearchResult { let parts: Vec<&str> = line.split('\\').collect();
repo: parts[0].to_string(), let res = SearchResult {
name: parts[1].to_string(), repo: parts[0].to_string(),
version: parts[2].to_string(), name: parts[1].to_string(),
description: parts[3].to_string(), version: parts[2].to_string(),
}; ood: None,
results_vec.push(res); description: parts[3].to_string(),
};
results_vec.push(res);
}
} }
if verbosity >= 1 { if verbosity >= 1 {
@ -182,30 +149,5 @@ pub fn repo_search(query: &str, options: Options) -> String {
); );
} }
// Format output results_vec
let results_vec = results_vec
.into_iter()
.map(|res| {
let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 4)
.subsequent_indent(" ");
format!(
"{}{}{} {}\n {}",
res.repo.purple().bold(),
"/".purple().bold(),
res.name.bold(),
res.version.green().bold(),
if res.description.is_empty() {
"No description".to_string()
} else {
wrap(&res.description, opts).join("\n")
},
)
})
.collect::<Vec<String>>();
if output.trim().is_empty() {
"".to_string()
} else {
results_vec.join("\n")
}
} }

Loading…
Cancel
Save