1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
pub mod arch;
pub mod errors;
pub mod license;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::prelude::*;
use std::path::Path;
use crate::{arch::Architecture, errors::Error, license::License};
use itertools::join;
use tar::Archive;
#[derive(Debug, Default)]
pub struct PkgInfo {
pub pkg_name: String,
pub pkg_base: Option<String>,
pub pkg_ver: String,
pub pkg_desc: String,
pub url: Option<String>,
pub size: u32,
pub arch: Architecture,
pub license: Option<License>,
pub conflict: Vec<String>,
pub provides: Vec<String>,
pub depend: Vec<String>,
pub opt_depend: Vec<String>,
pub make_depend: Vec<String>,
}
impl PkgInfo {
fn parse_file<R: Read>(entry: R) -> Result<Self, Error> {
let reader = io::BufReader::new(entry);
let mut pkg_info = PkgInfo::default();
for line in reader.lines() {
let line = line.map_err(Error::IoError)?;
let line = line.trim();
if line.starts_with('#') || !line.contains('=') {
continue;
}
let mut splitted = line.split('=');
let key = splitted.next().unwrap().trim();
let value: String = {
let v: String = join(splitted, "=");
v.trim().into()
};
pkg_info.add_field(key, value);
}
Ok(pkg_info)
}
fn add_field(&mut self, key: &str, value: String) {
if value.is_empty() {
return;
}
match key {
"pkgname" => self.pkg_name = value,
"pkgbase" => self.pkg_base = Some(value),
"pkgver" => self.pkg_ver = value,
"pkgdesc" => self.pkg_desc = value,
"url" => self.url = Some(value),
"size" => self.size = value.parse().expect("Couldn't parse size"),
"conflict" => self.conflict.push(value),
"provides" => self.provides.push(value),
"depend" => self.depend.push(value),
"optdepend" => self.opt_depend.push(value),
"makedepend" => self.make_depend.push(value),
"arch" => self.arch = Architecture::parse(value),
"license" => self.license = Some(License::parse(value)),
_ => {}
}
}
}
fn open_file<P: AsRef<Path>>(path: P) -> Result<File, Error> {
Ok(OpenOptions::new()
.read(true)
.open(&path)
.map_err(Error::IoError)?)
}
fn new_from_zst<P: AsRef<Path>>(path: P) -> Result<PkgInfo, Error> {
let mut a = Archive::new(zstd::Decoder::new(open_file(path)?).map_err(Error::IoError)?);
for file in a.entries().map_err(Error::IoError)? {
let file = file.map_err(Error::IoError)?;
if file.header().path().unwrap().to_str().unwrap() == ".PKGINFO" {
return PkgInfo::parse_file(file);
}
}
Err(Error::InvalidPackageFormat)
}
pub fn new_from_xz<P: AsRef<Path>>(path: P) -> Result<PkgInfo, Error> {
let mut a = Archive::new(xz::read::XzDecoder::new(open_file(path)?));
for file in a.entries().map_err(Error::IoError)? {
let file = file.map_err(Error::IoError)?;
if file.header().path().unwrap().to_str().unwrap() == ".PKGINFO" {
return PkgInfo::parse_file(file);
}
}
Err(Error::InvalidPackageFormat)
}
pub fn new<P: AsRef<str>>(path: P) -> Result<PkgInfo, Error> {
if path.as_ref().ends_with(".xz") {
return new_from_xz(Path::new(path.as_ref()));
}
new_from_zst(Path::new(path.as_ref()))
}