@@ 1,4 1,4 @@
-use std::{path::PathBuf, collections::{HashMap, HashSet}, fs};
+use std::{path::PathBuf, collections::{HashMap, HashSet}, fs, process::Command};
use regex::Regex;
use serde::{Deserialize, Serialize};
use walkdir::WalkDir;
@@ 51,12 51,12 @@ pub struct Database {
impl Database {
pub fn from_scan(folder: PathBuf) -> Self {
let mut db = Self {
- packages: HashMap::new(),
- depends: HashMap::new(),
- provides: HashMap::new(),
+ packages: HashMap::new(),
+ depends: HashMap::new(),
+ provides: HashMap::new(),
};
- // loop through all folders, but ignore broken entries
+ // loop through all folders, but ignore broken entries
for entry in WalkDir::new(folder).follow_links(true).into_iter().flatten(){
if entry.file_type().is_dir() {
// ignore folders themselves
@@ 69,6 69,7 @@ impl Database {
let file = entry.into_path();
+ println!("Scanning {}", file.to_str().unwrap());
let folder = file.parent().expect("Unable to get APKBUILD dir").to_path_buf();
let apk = Apkbuild::from_file(file);
@@ 142,46 143,83 @@ struct Apkbuild {
}
impl Apkbuild {
fn from_file(file: PathBuf) -> Self{
- let cont = fs::read_to_string(file)
- .expect("Failed to open APKBUILD");
-
- let mut apk = Self {
- name: easy_match("pkgname", &cont),
- description: easy_match("pkgdesc", &cont),
- rel: easy_match("pkgrel", &cont).parse()
- .expect("pkgrel should be a number"),
- arch: HashSet::new(),
- version: easy_match("pkgver", &cont),
- license: easy_match("license", &cont),
- url: easy_match("url", &cont),
- depends: HashSet::new(),
- provides: HashSet::new(),
- subs: Vec::new()
- };
+ let template = r#"pkgname=\"$pkgname\" pkgdesc=\"$pkgdesc\" pkgrel=\"$pkgrel\" pkgver=\"$pkgver\" url=\"$url\" license=\"$license\" arch=\"$arch\" depends=\"$depends $makedepends $checkdepends\" subpackages=\"$subpackages\" provides=\"$provides\""#;
+ let child = Command::new("sh")
+ .arg("-c")
+ .arg(format!("source {} && echo -e {}", file.to_str().unwrap(), template))
+ .output()
+ .expect("Unable to spawn APKBUILD parser");
+ if !child.status.success() {
+ // handle error
+ // NOTE: maybe fall back to regex parsing?
+ panic!("Failed to parse APKBUILD");
+ } else {
+ // parse the output & extract values
+ let cont = String::from_utf8(child.stdout)
+ .expect("Unable to parse otuput");
+
+ let mut apk = Self {
+ name: easy_match("pkgname", &cont),
+ description: easy_match("pkgdesc", &cont),
+ rel: easy_match_digit("pkgrel", &cont).parse()
+ .expect("pkgrel should be a number"),
+ arch: HashSet::new(),
+ version: easy_match("pkgver", &cont),
+ license: easy_match("license", &cont),
+ url: easy_match("url", &cont),
+ depends: HashSet::new(),
+ provides: HashSet::new(),
+ subs: Vec::new()
+ };
- for dep in easy_match_multi("depends", &cont) {
- apk.depends.insert(dep);
- }
- for provider in easy_match_multi("provides", &cont) {
- apk.provides.insert(provider);
- }
- for arch in easy_match_multi("arch", &cont) {
- apk.arch.insert(arch);
- }
- for subpkg in easy_match_multi("subpackages", &cont) {
- let subpkg = subpkg
- .replace("$pkgname", "${pkgname}")
- .replace("${pkgname}", &apk.name);
- apk.subs.push(subpkg);
- }
- apk
+ for dep in easy_match_multi("depends", &cont) {
+ let dep = dep.split('<').map(|s|s.to_string()).collect::<Vec<String>>().first().unwrap().to_string();
+ let dep = dep.split('>').map(|s|s.to_string()).collect::<Vec<String>>().first().unwrap().to_string();
+ let dep = dep.split('=').map(|s|s.to_string()).collect::<Vec<String>>().first().unwrap().to_string();
+
+ apk.depends.insert(dep);
+ }
+ for provider in easy_match_multi("provides", &cont) {
+ apk.provides.insert(provider);
+ }
+ for arch in easy_match_multi("arch", &cont) {
+ apk.arch.insert(arch);
+ }
+ for subpkg in easy_match_multi("subpackages", &cont) {
+ apk.subs.push(subpkg);
+ }
+
+ apk
+ }
}
}
/// extracts the first occurence of the given variable from the text
/// automatically removes whitespaces and quotes
/// panics if the variable is not defined
+/// uses \d
+fn easy_match_digit(var: &str, text: &str) -> String {
+ let re = Regex::new(&format!("{}=\"\\d+\"|{}=\\d+", var,var)).unwrap();
+ let extr = re.find(text)
+ .unwrap_or_else(|| panic!("Invalid APKBUILD, expected to find {}", var)).as_str();
+
+ // tidy up the value, by removing unneccesarry characters
+ // var="<want>"
+ let value = extr.split_at(var.len())
+ .1
+ .trim()
+ .trim_start_matches('=')
+ .trim_start_matches('"')
+ .trim_end_matches('"')
+ .trim();
+
+ String::from(value)
+}
+
+/// extracts the first occurence of the given variable from the text
+/// automatically removes whitespaces and quotes
+/// panics if the variable is not defined
fn easy_match(var: &str, text: &str) -> String {
let re = Regex::new(&format!("{}=\"[^\"]+\"|{}=[^\"\n]+\n", var,var)).unwrap();
let extr = re.find(text)