Add remote search for pacman and output

This commit is contained in:
Ella Dunbar 2025-07-26 21:52:07 -05:00
parent eac131ec71
commit 26506dbb28
3 changed files with 117 additions and 36 deletions

View file

@ -1,4 +1,4 @@
use std::ffi::OsString;
use std::error::Error;
pub mod pacman;
@ -7,12 +7,35 @@ pub struct Package {
pub name: String,
pub version: String,
pub description: Option<String>,
pub installed: Option<bool>,
}
impl std::fmt::Display for Package {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let end = "\x1b[0m";
let bold = "\x1b[1m";
let green = "\x1b[32m";
let magenta = "\x1b[35m";
let cyan = "\x1b[36m";
let installed = self.installed.unwrap_or(false);
let installed_string = if installed { "[installed] " } else { "" };
let repository_string = self.repository.join(" / ");
let description_string = self
.description
.clone()
.unwrap_or("No description provided".to_string());
write!(
f,
"{bold}{} {green}{} {cyan}{}{magenta}{}{end}\n {}",
self.name, self.version, installed_string, repository_string, description_string
)
}
}
pub trait Manager {
fn command_name(&self) -> OsString;
fn test_command(&mut self) -> Result<(), String>
where
Self: Sized;
fn remote_search(&self, query: &str) -> Result<Vec<Package>, &str>;
fn remote_search(query: &str) -> Result<Vec<Package>, Box<dyn Error>>;
}

View file

@ -1,6 +1,11 @@
use package_manager::{Manager, pacman::*};
use package_manager::{Manager, pacman::Pacman};
use std::env;
fn main() {
let m = Pacman;
print!("{:?}", m.manager_path().unwrap());
let arg = env::args().nth(1).unwrap();
let mut packages = Pacman::remote_search(&arg).unwrap();
packages.sort_by(|p1, p2| p1.name.cmp(&p2.name));
for package in packages {
println!("{}", package);
}
}

View file

@ -1,35 +1,88 @@
use crate::*;
use std::str::FromStr;
use std::{ffi::OsString, process::Command};
use std::{collections::HashMap, process::Command};
pub struct Pacman {
command_exists: bool,
struct UnmergedPackage {
repository: String,
name: String,
version: String,
description: Option<String>,
installed: Option<bool>,
}
pub struct Pacman;
impl Manager for Pacman {
fn command_name(&self) -> OsString {
return OsString::from_str("pacman").unwrap();
fn remote_search(query: &str) -> Result<Vec<Package>, Box<dyn Error>> {
let output = Command::new("pacman").arg("-Ss").arg(query).output()?;
let stdout = String::from_utf8(output.stdout)?;
let lines: Vec<String> = stdout.lines().map(|s| s.to_string()).collect();
let mut unmerged_packages = Vec::new();
let mut i = 0;
while i + 1 < lines.len() {
let start_line = &lines[i];
let description_line = &lines[i + 1];
// split into repo + name, version, and install
let parts: Vec<&str> = start_line.split_whitespace().collect();
let repo_name = parts[0];
let version = parts[1].to_string();
let installed = Some((parts.len() > 2) && parts[2].starts_with("[installed"));
// split repo + name into repo and name
let repo_name_parts: Vec<&str> = repo_name.split("/").collect();
let repository = repo_name_parts[0].to_string();
let name = repo_name_parts[1].to_string();
// strip description
let description = Some(description_line.trim().to_string());
// create package
let package = UnmergedPackage {
repository,
name,
version,
description,
installed,
};
unmerged_packages.push(package);
i += 2;
}
fn test_command(&mut self) -> Result<(), String>
where
Self: Sized {
if self.command_exists { return Ok(()); }
let output = Command::new("which").arg(self.command_name()).output();
match output {
Ok(output) => {
if output.status.success() {
self.command_exists = true;
return Ok(());
} else {
let err = format!("{} could not be found in path", self.command_name().to_string_lossy());
return Err(err);
}
}
Err(_) => return Err("Existence check could not be run.".to_string()),
}
}
fn remote_search(&self, query: &str) -> Result<Vec<Package>, &str> {
if self.command_exists()
let output = Command::new
Ok(merge_packages(unmerged_packages))
}
}
fn process_version(version: &str) -> String {
if version.ends_with(".1") {
version[..version.len() - 2].to_string()
} else {
version.to_string()
}
}
fn merge_packages(unmerged: Vec<UnmergedPackage>) -> Vec<Package> {
let mut groups: HashMap<String, Vec<UnmergedPackage>> = HashMap::new();
for package in unmerged {
groups
.entry(package.name.clone())
.or_insert_with(Vec::new)
.push(package);
}
let mut result = Vec::new();
for (name, packages) in groups {
let processed_version = process_version(&packages[0].version);
let repositories: Vec<String> = packages.iter().map(|p| p.repository.clone()).collect();
let description = packages[0].description.clone();
let installed = packages[0].installed;
result.push(Package {
name,
version: processed_version,
repository: repositories,
description,
installed,
});
}
result
}