1use crate::dep::Dependencies;
20use crate::files::Files;
21use crate::internal::header::Header;
22use crate::{RpmErrorKind, Tag, TagData};
23use std::convert::TryFrom;
24use std::hash::{Hash, Hasher};
25use std::{fmt, path::Path, time};
26
27pub struct Package {
48 header: Header,
49}
50
51impl Package {
52 pub(crate) fn from_header(h: &Header) -> Self {
53 Package { header: h.clone() }
54 }
55
56 pub fn from_file(path: &Path) -> Result<Self, RpmErrorKind> {
58 let header = Header::from_file(path)?;
59 Ok(Package { header })
60 }
61
62 pub fn get(&self, tag: Tag) -> Option<TagData<'_>> {
64 self.header.get(tag)
65 }
66
67 pub fn name(&self) -> &str {
69 self.header
70 .get(Tag::NAME)
71 .expect("NAME tag missing")
72 .as_str()
73 .expect("NAME tag is not a string")
74 }
75
76 pub fn epoch(&self) -> Option<i32> {
78 self.header
79 .get(Tag::EPOCH)
80 .map(|d| d.as_int32().expect("EPOCH tag is not an int32"))
81 }
82
83 pub fn version(&self) -> &str {
85 self.header
86 .get(Tag::VERSION)
87 .expect("VERSION tag missing")
88 .as_str()
89 .expect("VERSION tag is not a string")
90 }
91
92 pub fn release(&self) -> &str {
94 self.header
95 .get(Tag::RELEASE)
96 .expect("RELEASE tag missing")
97 .as_str()
98 .expect("RELEASE tag is not a string")
99 }
100
101 pub fn arch(&self) -> Option<&str> {
103 self.header
104 .get(Tag::ARCH)
105 .map(|d| d.as_str().expect("ARCH tag is not a string"))
106 }
107
108 pub fn evr(&self) -> String {
110 if let Some(epoch) = self.epoch() {
111 format!("{}:{}-{}", epoch, self.version(), self.release())
112 } else {
113 format!("{}-{}", self.version(), self.release())
114 }
115 }
116
117 pub fn nevra(&self) -> String {
119 if let Some(arch) = self.arch() {
120 format!("{}-{}.{}", self.name(), self.evr(), arch)
121 } else {
122 format!("{}-{}", self.name(), self.evr())
123 }
124 }
125
126 pub fn license(&self) -> &str {
128 self.header
129 .get(Tag::LICENSE)
130 .expect("LICENSE tag missing")
131 .as_str()
132 .expect("LICENSE tag is not a string")
133 }
134
135 pub fn summary(&self) -> &str {
137 self.header
138 .get(Tag::SUMMARY)
139 .expect("SUMMARY tag missing")
140 .as_str()
141 .expect("SUMMARY tag is not a string")
142 }
143
144 pub fn description(&self) -> &str {
146 self.header
147 .get(Tag::DESCRIPTION)
148 .expect("DESCRIPTION tag missing")
149 .as_str()
150 .expect("DESCRIPTION tag is not a string")
151 }
152
153 pub fn files(&self) -> Files {
155 Files::from_header(&self.header)
156 }
157
158 pub fn requires(&self) -> Dependencies {
160 Dependencies::from_header(&self.header, Tag::REQUIRENAME)
161 }
162
163 pub fn provides(&self) -> Dependencies {
165 Dependencies::from_header(&self.header, Tag::PROVIDENAME)
166 }
167
168 pub fn conflicts(&self) -> Dependencies {
170 Dependencies::from_header(&self.header, Tag::CONFLICTNAME)
171 }
172
173 pub fn obsoletes(&self) -> Dependencies {
175 Dependencies::from_header(&self.header, Tag::OBSOLETENAME)
176 }
177
178 pub fn recommends(&self) -> Dependencies {
180 Dependencies::from_header(&self.header, Tag::RECOMMENDNAME)
181 }
182
183 pub fn suggests(&self) -> Dependencies {
185 Dependencies::from_header(&self.header, Tag::SUGGESTNAME)
186 }
187
188 pub fn supplements(&self) -> Dependencies {
190 Dependencies::from_header(&self.header, Tag::SUPPLEMENTNAME)
191 }
192
193 pub fn enhances(&self) -> Dependencies {
195 Dependencies::from_header(&self.header, Tag::ENHANCENAME)
196 }
197
198 pub fn buildtime(&self) -> time::SystemTime {
200 let buildtime = self
201 .header
202 .get(Tag::BUILDTIME)
203 .expect("BUILDTIME tag missing")
204 .as_int32()
205 .expect("BUILDTIME tag is not an int32");
206 let buildtime = u64::try_from(buildtime).expect("negative build time");
207 time::SystemTime::UNIX_EPOCH + time::Duration::new(buildtime, 0)
208 }
209}
210
211impl Clone for Package {
212 fn clone(&self) -> Self {
213 Package {
214 header: self.header.clone(),
215 }
216 }
217}
218
219impl fmt::Debug for Package {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 f.debug_struct("Package")
222 .field("name", &self.name())
223 .field("epoch", &self.epoch())
224 .field("version", &self.version())
225 .field("release", &self.release())
226 .field("arch", &self.arch())
227 .finish()
228 }
229}
230
231impl fmt::Display for Package {
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233 write!(f, "{}", self.nevra())
234 }
235}
236
237impl PartialEq for Package {
238 fn eq(&self, other: &Self) -> bool {
239 self.name() == other.name()
240 && self.epoch() == other.epoch()
241 && self.version() == other.version()
242 && self.release() == other.release()
243 && self.arch() == other.arch()
244 }
245}
246
247impl Eq for Package {}
248
249impl Hash for Package {
250 fn hash<H: Hasher>(&self, state: &mut H) {
251 self.name().hash(state);
252 self.epoch().hash(state);
253 self.version().hash(state);
254 self.release().hash(state);
255 self.arch().hash(state);
256 }
257}