Browse Source

Add AUR search

schaeferpp 1 year ago
parent
commit
2cf8564431
6 changed files with 1192 additions and 65 deletions
  1. 972 4
      Cargo.lock
  2. 6 1
      Cargo.toml
  3. 87 0
      src/alpm.rs
  4. 82 0
      src/aur.rs
  5. 31 56
      src/lib.rs
  6. 14 4
      src/main.rs

File diff suppressed because it is too large
+ 972 - 4
Cargo.lock


+ 6 - 1
Cargo.toml

@@ -4,6 +4,11 @@ version = "0.1.0"
 authors = ["schaeferpp <schaefer.pp@gmail.com>"]
 
 [dependencies]
-libalpm = "0.1.2"
+# libalpm = "0.1.2"
+libalpm = { path = "../alpm/libalpm" }
 clap = "2.31.2"
 rust-ini = "0.12.1"
+serde = "1.0.66"
+serde_derive = "1.0.66"
+serde_json = "1.0"
+reqwest = "0.8.6"

+ 87 - 0
src/alpm.rs

@@ -0,0 +1,87 @@
+//
+// alpm.rs
+// Copyright (C) 2018 Paul Schaefer <schaefer.pp@gmail.com>
+// Distributed under terms of the LGPL-3.0+ license.
+//
+
+use libalpm;
+use libalpm::Alpm;
+use ini::Ini;
+
+const DEFAULT_DB_PATH: &'static str = "/var/lib/pacman/";
+const DEFAULT_ROOT_DIR: &'static str = "/";
+
+#[derive(Debug)]
+pub struct RepoPackage {
+    pub repo: String,
+    pub name: String,
+    pub description: String,
+    pub version: String,
+}
+
+impl RepoPackage {
+}
+
+
+pub struct Context {
+    alpm: libalpm::Alpm,
+}
+
+impl Context {
+    pub fn new() -> Result<Context, super::Error> {
+        Self::new_from_conf("/etc/pacman.conf")
+    }
+
+    pub fn new_from_conf(conf: &str) -> Result<Context, super::Error> {
+        let conf = Ini::load_from_file(conf).or_else(|err| Err(super::Error::ConfigError(err)))?;
+        let options: Option<_> = conf.section(Some("options"));
+        let rootdir = if let Some(options) = options {
+            match options.get("RootDir") {
+                Some(dir) => dir,
+                None => DEFAULT_ROOT_DIR,
+            }
+        } else {
+            DEFAULT_ROOT_DIR
+        };
+
+        let dbpath = if let Some(options) = options {
+            match options.get("DBPath") {
+                Some(dir) => dir,
+                None => DEFAULT_DB_PATH,
+            }
+        } else {
+            DEFAULT_DB_PATH
+        };
+
+        let alpm: libalpm::Alpm =
+            Alpm::new(rootdir, dbpath).or_else(|err| Err(super::Error::AlpmError(err)))?;
+        if let Err(e) = alpm.set_arch(&libalpm::util::uname().machine().to_owned()).or_else(|e| Err(super::Error::AlpmError(e))) {
+            return Err(e);
+        }
+        Ok(Context { alpm })
+    }
+
+    pub fn search(&self, query: &str) -> Vec<RepoPackage> {
+        self.alpm
+            .local_db()
+            .search(String::from(query).split(" ").collect())
+            .unwrap_or_default()
+            .iter()
+            .map(|p| RepoPackage {
+                repo: String::from(format!("{:?}", p.db(&self.alpm).name().unwrap_or_default())),
+                name: String::from(p.name()),
+                description: String::from(p.description()),
+                version: String::from(format!("{}", p.version())),
+            })
+            .collect()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+
+	#[test]
+	fn it_works() {
+	}
+}

+ 82 - 0
src/aur.rs

@@ -3,6 +3,88 @@
 // Copyright (C) 2018 Paul Schaefer <schaefer.pp@gmail.com>
 // Distributed under terms of the LGPL-3.0+ license.
 //
+use reqwest;
+use std::convert::From;
+use std::io::Read;
+use serde_json;
+
+pub enum SearchType {
+    Name,
+    NameDesc,
+    Maintainer
+}
+
+#[derive(Deserialize, Default, Debug, PartialEq)]
+pub struct AurPackage {
+    #[serde(rename = "ID")]
+    pub id: u32,
+    #[serde(rename = "Name")]
+    pub name: String,
+    #[serde(rename = "Description")]
+    pub description: Option<String>,
+    // #[serde(rename = "ID")]
+    // description: String,
+    // #[serde(rename = "Maintainer")]
+    // maintainer: String,
+    #[serde(rename = "URLPath")]
+    pub url_path: String,
+    #[serde(rename = "URL")]
+    pub url: Option<String>,
+
+    #[serde(rename = "NumVotes")]
+    pub num_votes: u32,
+
+    #[serde(rename = "Popularity")]
+    pub popularity: f32,
+
+    #[serde(rename = "Version")]
+    pub version: Option<String>,
+
+    #[serde(rename = "OutOfDate")]
+    pub out_of_date: Option<u32>,
+    // first_submitted: u32,
+}
+
+impl AurPackage {
+    pub fn get_name(&self) -> &String {
+        &self.name
+    }
+    pub fn get_description(&self) -> Option<&String> {
+        match &self.description {
+            Some(d) => Some(&d),
+            None => None
+        }
+    }
+}
+
+#[derive(Deserialize, Default, Debug)]
+struct AurResponse {
+    resultcount: u32,
+    results: Vec<AurPackage>,
+    version: u32
+}
+
+const AUR_URL: &str = "https://aur.archlinux.org";
+
+pub fn search(query: &str, by: SearchType) -> Result<Vec<AurPackage>, super::Error> {
+    let url = format!("{}/rpc/?v=5&type={}&by={}&arg={}", AUR_URL, "search", match by {
+        SearchType::Name => "name",
+        SearchType::NameDesc => "name-desc",
+        SearchType::Maintainer => "maintainer",
+    }, query);
+
+    // TODO search with every parameter once, merge later
+    let mut response = reqwest::get(&url).or_else(|e| Err(super::Error::HttpError(e)))?;
+    if response.status().is_success() {
+        let mut json = String::new();
+        response.read_to_string(&mut json).or_else(|e| Err(super::Error::GenericError(format!("Error fetching HTTP response for AUR query: {}", e))))?;
+        let result: AurResponse = serde_json::from_str(&json).or_else(|e| Err(super::Error::FormatError(e)))?;
+        // println!("{:?}", result);
+        Ok(result.results)
+    } else {
+        Err(super::Error::AurError(String::from("Unknown")))
+    }
+}
 
 #[cfg(test)]
 mod tests {

+ 31 - 56
src/lib.rs

@@ -7,78 +7,53 @@
 extern crate clap;
 extern crate ini;
 extern crate libalpm;
+extern crate reqwest;
+#[macro_use]
+extern crate serde_derive;
 
-use ini::Ini;
-use libalpm::Alpm;
-use std::prelude::*;
+extern crate serde;
+extern crate serde_json;
 
-mod aur;
+pub mod aur;
+pub mod alpm;
 
 pub use aur::*;
 
+use std::fmt;
+
 #[derive(Debug)]
 pub enum Error {
     ConfigError(ini::ini::Error),
+    HttpError(reqwest::Error),
     AlpmError(libalpm::Error),
+    FormatError(serde_json::error::Error),
+    AurError(String),
+    GenericError(String),
 }
 
-pub struct Context {
-    alpm: libalpm::Alpm,
-}
-
-const DEFAULT_DB_PATH: &'static str = "/var/lib/pacman/";
-const DEFAULT_ROOT_DIR: &'static str = "/";
-
-impl Context {
-    pub fn new() -> Result<Context, Error> {
-        Self::new_from_conf("/etc/pacman.conf")
-    }
-
-    pub fn new_from_conf(conf: &str) -> Result<Context, Error> {
-        let conf = Ini::load_from_file(conf).or_else(|err| Err(Error::ConfigError(err)))?;
-        let section = Some(String::from("options"));
-        let rootdir = if let Some(options) = conf.section(Some("options")) {
-            match options.get("RootDir") {
-                Some(dir) => dir,
-                None => DEFAULT_ROOT_DIR,
-            }
-        } else {
-            DEFAULT_ROOT_DIR
-        };
-
-        let dbpath = if let Some(options) = conf.section(Some("options")) {
-            match options.get("DBPath") {
-                Some(dir) => dir,
-                None => DEFAULT_DB_PATH,
-            }
-        } else {
-            DEFAULT_DB_PATH
-        };
 
-        let alpm: libalpm::Alpm =
-            Alpm::new(rootdir, dbpath).or_else(|err| Err(Error::AlpmError(err)))?;
-        alpm.set_arch(&libalpm::util::uname().machine().to_owned());
-        Ok(Context { alpm })
-    }
+pub enum Package {
+    ALPM(alpm::RepoPackage),
+    AUR(aur::AurPackage),
+}
 
-    pub fn search(&self, query: &str) -> Vec<Packet> {
-        self.alpm
-            .local_db()
-            .search(String::from(query).split(" ").collect())
-            .unwrap_or_default()
-            .iter()
-            .map(|p| Packet {
-                name: String::from(p.name()),
-                version: String::from(format!("{}", p.version())),
-            })
-            .collect()
+impl fmt::Debug for Package {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Package::ALPM(pkg) => write!(f, "Package::ALPM {:?}", pkg),
+            Package::AUR(pkg) => write!(f, "Package::AUR {:?}", pkg),
+        }
     }
 }
 
-#[derive(Debug)]
-pub struct Packet {
-    pub name: String,
-    pub version: String,
+pub fn search(query: &str) -> Result<Vec<Package>, Error> {
+    let repopkgs = alpm::Context::new()?.search(query);
+    let aurpkgs = aur::search(query, aur::SearchType::NameDesc)?;
+    let mut res: Vec<Package> = Vec::new();
+    repopkgs.into_iter().map(|pkg| Package::ALPM(pkg)).for_each(|pkg| res.push(pkg));
+    aurpkgs.into_iter().map(|pkg| Package::AUR(pkg)).for_each(|pkg| res.push(pkg));
+    // res.append(.collect());
+    Ok(res)
 }
 
 #[cfg(test)]

+ 14 - 4
src/main.rs

@@ -22,15 +22,25 @@ pub fn main() {
         )
         .get_matches();
 
-    let ctxt = paurl::Context::new().unwrap();
+    let ctxt = paurl::alpm::Context::new().unwrap();
 
     if let Some(matches) = matches.subcommand_matches("search") {
         let matches: &clap::ArgMatches = matches;
         let querystring = matches
             .value_of("query")
             .expect("Please specify a query string!");
-        println!("{:?}", ctxt.search(querystring));
+        for pkg in paurl::search(querystring).unwrap() {
+            match pkg {
+                paurl::Package::AUR(pkg) => {
+                    println!("aur/{}", pkg.name);
+                    println!("  {}", pkg.description.unwrap_or_default());
+                },
+                paurl::Package::ALPM(pkg) => {
+                    println!("{}/{}", pkg.repo, pkg.name);
+                    println!("  {}", pkg.description);
+                    // println!("  ", pkg.get_description());
+                },
+            }
+        }
     }
-
-    println!("Hello World");
 }