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",
"serde",
"spinoff",
"strsim",
"textwrap",
"toml",
"ureq",

@ -35,4 +35,5 @@ spinoff = { version = "0.5.2", default-features = false }
textwrap = { version = "0.15.0", features = [ "terminal_size", "smawk" ] }
chrono = { version = "0.4.22", default-features = false, features = [ "clock", "std", "wasmbind" ] }
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)]
use crate::operations::SearchBy;
use clap::{Parser, Subcommand, ValueHint};
#[derive(Debug, Clone, Parser)]
@ -105,11 +104,7 @@ pub struct SearchArgs {
/// The string the package must match in the search
#[clap(required = true)]
pub search: Vec<String>,
/// Sets the search-by directive for searching the AUR only
#[clap(long, short, possible_values = SearchBy::variants())]
pub by: Option<SearchBy>,
pub search: String,
}
#[derive(Default, Debug, Clone, Parser)]

@ -1,4 +1,3 @@
use crate::operations::SearchBy;
use std::sync::Arc;
#[derive(serde::Deserialize, Debug, Clone)]
@ -76,7 +75,7 @@ pub fn rpcinfo(pkg: &str) -> InfoResults {
}
/// 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
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
agent
.get(&format!(
"https://aur.archlinux.org/rpc/?v=5&type=search&by={}&arg={}",
by, pkg
"https://aur.archlinux.org/rpc/?v=5&type=search&arg={}",
pkg
))
.call()
.unwrap()

@ -4,8 +4,11 @@
use args::Args;
use clap::{CommandFactory, Parser};
use clap_complete::{Generator, Shell};
use strsim::sorensen_dice;
use internal::commands::ShellCommand;
use internal::error::SilentUnwrap;
use std::env;
use std::fs;
use std::path::Path;
@ -17,6 +20,7 @@ use crate::args::{
use crate::internal::exit_code::AppExitCode;
use crate::internal::utils::pager;
use crate::internal::{detect, init, sort, start_sudoloop, structs::Options};
use crate::operations::ResultsVec;
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
@ -186,7 +190,7 @@ fn cmd_remove(args: RemoveArgs, options: Options) {
fn cmd_search(args: &SearchArgs, options: Options) {
// Initialise variables
let query_string = args.search.join(" ");
let query_string = &args.search;
// Logic for searching
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);
// Search repos
let ret = operations::search(&query_string, options);
let ret = operations::search(query_string, options);
rsp.stop_bold("Repo search complete");
ret
} else {
"".to_string()
Vec::new()
};
// Start AUR spinner
@ -214,30 +218,46 @@ fn cmd_search(args: &SearchArgs, options: Options) {
let asp = spinner!("Searching AUR for {}", query_string);
// 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");
ret
} 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`
let text = if internal::uwu_enabled() {
uwu!(results.trim())
uwu!(results.to_string())
} else {
results.trim().to_string()
results.to_string()
};
let text = text.trim().to_string();
println!("{}", text);
// 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
#[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 clean::*;
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 upgrade::*;

@ -3,7 +3,6 @@ use colored::Colorize;
use textwrap::wrap;
use std::fmt::Display;
use std::str::FromStr;
use crate::internal::commands::ShellCommand;
use crate::internal::error::SilentUnwrap;
@ -12,72 +11,69 @@ use crate::internal::rpc::rpcsearch;
use crate::{log, Options};
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, Copy)]
pub enum SearchBy {
Name,
NameDesc,
Maintainer,
Depends,
MakeDepends,
OptDepends,
CheckDepends,
#[derive(Debug, Clone)]
pub struct SearchResult {
pub repo: String,
pub name: String,
pub version: String,
pub ood: Option<usize>,
pub description: String,
}
impl FromStr for SearchBy {
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 {
impl Display for SearchResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Name => write!(f, "name"),
Self::NameDesc => write!(f, "name-desc"),
Self::Maintainer => write!(f, "maintainer"),
Self::Depends => write!(f, "depends"),
Self::MakeDepends => write!(f, "makedepends"),
Self::OptDepends => write!(f, "optdepends"),
Self::CheckDepends => write!(f, "checkdepends"),
}
let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 4)
.subsequent_indent(" ");
let description = wrap(&self.description, opts).join("\n");
write!(
f,
"{}{} {} {}\n {}",
if self.repo == "aur" {
(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 {
fn default() -> Self {
Self::NameDesc
pub struct ResultsVec(pub Vec<SearchResult>);
impl From<Vec<SearchResult>> for ResultsVec {
fn from(v: Vec<SearchResult>) -> Self {
Self(v)
}
}
impl SearchBy {
pub fn variants() -> Vec<&'static str> {
vec![
"name",
"name-desc",
"maintainer",
"depends",
"makedepends",
"optdepends",
"checkdepends",
]
impl Display for ResultsVec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for result in &self.0 {
writeln!(f, "{}", result)?;
}
Ok(())
}
}
#[allow(clippy::module_name_repetitions)]
/// 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
let res = rpcsearch(query, by);
let res = rpcsearch(query);
// Get verbosity
let verbosity = options.verbosity;
@ -85,36 +81,17 @@ pub fn aur_search(query: &str, options: Options, by: SearchBy) -> String {
// Format output
let mut results_vec = vec![];
for package in &res.results {
// Define wrapping options
let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 4)
.subsequent_indent(" ");
let result = format!(
"{}{} {} {}\n {}",
"aur/".cyan().bold(),
package.name.bold(),
package.version.green().bold(),
if package.out_of_date.is_some() {
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"),
);
let result = SearchResult {
repo: "aur".to_string(),
name: package.name.to_string(),
version: package.version.to_string(),
ood: package.out_of_date,
description: package
.description
.as_ref()
.unwrap_or(&"No description".to_string())
.to_string(),
};
results_vec.push(result);
}
@ -126,19 +103,12 @@ pub fn aur_search(query: &str, options: Options, by: SearchBy) -> String {
);
}
results_vec.join("\n")
}
struct SearchResult {
repo: String,
name: String,
version: String,
description: String,
results_vec
}
#[allow(clippy::module_name_repetitions)]
/// 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
let verbosity = options.verbosity;
@ -156,22 +126,19 @@ pub fn repo_search(query: &str, options: Options) -> String {
// Initialise results vector
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
for line in lines {
let parts: Vec<&str> = line.split('\\').collect();
let res = SearchResult {
repo: parts[0].to_string(),
name: parts[1].to_string(),
version: parts[2].to_string(),
description: parts[3].to_string(),
};
results_vec.push(res);
if line.contains('\\') {
let parts: Vec<&str> = line.split('\\').collect();
let res = SearchResult {
repo: parts[0].to_string(),
name: parts[1].to_string(),
version: parts[2].to_string(),
ood: None,
description: parts[3].to_string(),
};
results_vec.push(res);
}
}
if verbosity >= 1 {
@ -182,30 +149,5 @@ pub fn repo_search(query: &str, options: Options) -> String {
);
}
// Format output
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")
}
results_vec
}

Loading…
Cancel
Save