pkgutils/
package.rs

1use libflate::gzip::Decoder;
2use std::fs::File;
3use std::io::BufReader;
4use std::io::{self, Error, ErrorKind, Read};
5use std::path::Path;
6use std::path::PathBuf;
7use tar::{Archive, EntryType};
8
9use crate::packagemeta::PackageMeta;
10
11pub struct Package {
12    archive: Archive<Decoder<BufReader<File>>>,
13    path: PathBuf,
14    meta: Option<PackageMeta>,
15}
16
17impl Package {
18    pub fn from_path<P: AsRef<Path>>(path: P) -> io::Result<Self> {
19        let file = File::open(&path)?;
20        let decoder = Decoder::new(BufReader::new(file))?;
21
22        let mut ar = Archive::new(decoder);
23        ar.set_preserve_permissions(true);
24        Ok(Package {
25            archive: ar,
26            path: path.as_ref().to_path_buf(),
27            meta: None,
28        })
29    }
30
31    pub fn install<P: AsRef<Path>>(&mut self, dest: P) -> io::Result<()> {
32        self.archive.unpack(dest)?;
33        Ok(())
34    }
35
36    pub fn list(&mut self) -> io::Result<()> {
37        for i in self.archive.entries()? {
38            println!("{}", i?.path()?.display());
39        }
40        Ok(())
41    }
42
43    pub fn archive(&self) -> &Archive<Decoder<BufReader<File>>> {
44        &self.archive
45    }
46
47    pub fn path(&self) -> &Path {
48        &self.path
49    }
50
51    pub fn meta(&mut self) -> io::Result<&PackageMeta> {
52        if self.meta.is_none() {
53            let mut toml = None;
54            for entry in self.archive.entries()? {
55                let mut entry = entry?;
56                if entry.header().entry_type() != EntryType::Directory
57                    && entry.path()?.starts_with("pkg")
58                {
59                    if toml.is_none() {
60                        let mut text = String::new();
61                        entry.read_to_string(&mut text)?;
62                        toml = Some(text);
63                    } else {
64                        return Err(Error::new(
65                            ErrorKind::Other,
66                            "Multiple metadata files in package",
67                        ));
68                    }
69                }
70            }
71
72            if let Some(toml) = toml {
73                self.meta = Some(
74                    PackageMeta::from_toml(&toml).map_err(|e| Error::new(ErrorKind::Other, e))?,
75                );
76            } else {
77                return Err(Error::new(
78                    ErrorKind::NotFound,
79                    "Package metadata not found",
80                ));
81            }
82        }
83
84        Ok(self.meta.as_ref().unwrap())
85    }
86}