pkg_utils/
db.rs

1//! Db abstraction, providing access and search. Currently
2//! mostly unoptimized, contribs here would be appreciated
3
4use std::io::Read;
5use std::fs::{File, read_dir};
6use std::path::{Path, PathBuf};
7
8use flate2::read::GzDecoder;
9use itertools::process_results;
10use rayon::prelude::*;
11use tar::Archive;
12
13use Provide;
14use error::Result;
15use package::MetaPackage;
16
17#[derive(Debug)]
18pub struct Db {
19    path: PathBuf,
20    pub packages: Vec<MetaPackage>
21}
22
23impl Db {
24    /// Pass this `"/var/lib/pacman"`. WIP: This is multithreaded, but
25    /// threads are hard and this is probably naive.
26    pub fn sync_dbs(location: impl AsRef<Path>) -> Result<Vec<Db>> {
27        let mut sync_dbs_path = PathBuf::from(location.as_ref());
28        sync_dbs_path.push("sync");
29        
30        let mut dbs = Vec::new();
31        for db_path in read_dir(&sync_dbs_path)? {
32            let db_path = db_path?.path();
33            if let Some(extension) = db_path.extension() {
34                if extension == "db" {
35                    dbs.push(db_path.to_path_buf());
36                }
37            }
38        }
39        
40        let dbs: Vec<_> = dbs.par_iter()
41            .map(|db_path| -> Result<Db> {
42                let packages = Db::sync_db_pkgs(&db_path)?;
43                Ok(Db {
44                    path: db_path.to_path_buf(),
45                    packages: packages
46                })
47            })
48            .collect();
49        
50        process_results(dbs, |iter| iter.collect() )
51    }
52    
53    /// Pass this `"/var/lib/pacman"`.
54    pub fn local_db(location: impl AsRef<Path>) -> Result<Db> {
55        let mut local_db_path = PathBuf::from(location.as_ref());
56        local_db_path.push("local");
57        let packages = Db::local_db_pkgs(&local_db_path)?;
58        Ok(Db {
59            path: local_db_path,
60            packages: packages
61        })
62    }
63    
64    fn local_db_pkgs(path: impl AsRef<Path>) -> Result<Vec<MetaPackage>> {
65        let mut pkgs = Vec::with_capacity(200);
66        let mut iter_count = 0;
67        
68        for spec_dir in read_dir(&path)? {
69            let spec_dir = spec_dir?;
70            let mut pkg_path = spec_dir.path();
71            
72            if pkg_path != Path::join(path.as_ref(), "ALPM_DB_VERSION") {
73                iter_count += 1;
74                if iter_count >= 200 {
75                    iter_count = 0;
76                    pkgs.reserve(200);
77                }
78                pkg_path.push("desc");
79                let mut file = File::open(pkg_path)?;
80                
81                let mut data = String::new();
82                file.read_to_string(&mut data)?;
83                pkgs.push(MetaPackage::parse_pkgdesc(data)?);
84            }
85        }
86        Ok(pkgs)
87    }
88    
89    fn sync_db_pkgs(path: impl AsRef<Path>) -> Result<Vec<MetaPackage>> {
90        let db = File::open(&path)?;
91        let db = GzDecoder::new(db);
92        let mut db = Archive::new(db);
93        
94        let mut pkgs = Vec::with_capacity(200);
95        let mut iter_count = 0;
96        
97        for desc in db.entries()? {
98            let mut desc = desc?;
99            let path = desc.path()?.to_path_buf();
100            
101            if path.to_string_lossy() != "ALPM_DB_VERSION" {
102                iter_count += 1;
103                if iter_count >= 200 {
104                    iter_count = 0;
105                    pkgs.reserve(200);
106                }
107                let mut data = String::new();
108                desc.read_to_string(&mut data)?;
109                
110                pkgs.push(MetaPackage::parse_pkgdesc(data)?);
111            }
112        }
113        Ok(pkgs)
114    }
115    
116    /// Get some package metadata from this Db. Helper function over this
117    /// function's source
118    pub fn pkg(&self, pkgname: impl AsRef<str>) -> Option<&MetaPackage> {
119        self.packages.iter()
120            .find(|pkg| pkg.name == pkgname.as_ref() )
121    }
122    
123    /// Determines if a `Provide` is provided by this db.
124    /// ```rust
125    /// # use pkg_utils::{db::Db, Provide};
126    /// let db = Db::local_db("/var/lib/pacman").unwrap();
127    /// // This indicates any version
128    /// db.provides(&Provide::parse("java-environment").unwrap());
129    /// // Version 8 only
130    /// db.provides(&Provide::parse("java-environment=8").unwrap());
131    /// ```
132    pub fn provides(&self, provide: &Provide) -> bool {
133        self.packages.iter()
134            .any(|pkg| pkg.satisfies(provide) )
135    }
136}