1use chrono::{DateTime, Utc};
2use serde::de::Visitor;
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use std::fmt::{Display, Formatter};
5use std::str::FromStr;
6use std::sync::Arc;
7
8#[derive(Clone, Debug, PartialEq)]
9pub struct DependencyConstraintsParseError {
10 source: String,
11}
12
13impl DependencyConstraintsParseError {
14 fn new(source: &str) -> Self {
15 DependencyConstraintsParseError {
16 source: source.to_owned(),
17 }
18 }
19}
20
21impl Display for DependencyConstraintsParseError {
22 fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
23 write!(
24 formatter,
25 "Cannot parse dependency constraint {}",
26 &self.source
27 )
28 }
29}
30
31impl std::error::Error for DependencyConstraintsParseError {}
32
33#[derive(Clone, Copy, Eq, PartialEq, Debug)]
34pub enum DependencyConstraints {
35 LessThan,
37 MoreThan,
39 Equals,
41 MoreOrEqualsThan,
43 LessOrEqualsThan,
45}
46
47impl FromStr for DependencyConstraints {
48 type Err = DependencyConstraintsParseError;
49
50 fn from_str(s: &str) -> Result<Self, Self::Err> {
51 if s == "<" {
52 Ok(DependencyConstraints::LessThan)
53 } else if s == ">" {
54 Ok(DependencyConstraints::MoreThan)
55 } else if s == "=" {
56 Ok(DependencyConstraints::Equals)
57 } else if s == ">=" {
58 Ok(DependencyConstraints::MoreOrEqualsThan)
59 } else if s == "<=" {
60 Ok(DependencyConstraints::LessOrEqualsThan)
61 } else {
62 Err(DependencyConstraintsParseError::new(s))
63 }
64 }
65}
66
67impl Display for DependencyConstraints {
68 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
69 f.write_str(match self {
70 DependencyConstraints::LessThan => "<",
71 DependencyConstraints::MoreThan => ">",
72 DependencyConstraints::Equals => "=",
73 DependencyConstraints::MoreOrEqualsThan => ">=",
74 DependencyConstraints::LessOrEqualsThan => "<=",
75 })
76 }
77}
78
79#[derive(Clone, Debug, PartialEq)]
80pub enum DependencyVersionParseError {
81 ConstraintNotFound,
82 VersionNotFound,
83}
84
85impl Display for DependencyVersionParseError {
86 fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
87 match self {
88 DependencyVersionParseError::ConstraintNotFound => {
89 write!(formatter, "Constraint not found")
90 }
91 DependencyVersionParseError::VersionNotFound => write!(formatter, "Version not found"),
92 }
93 }
94}
95
96impl std::error::Error for DependencyVersionParseError {}
97
98#[derive(Clone, Eq, PartialEq, Debug)]
99pub struct DependencyVersion {
100 pub constraint: DependencyConstraints,
101 pub version: String,
102}
103
104impl FromStr for DependencyVersion {
105 type Err = DependencyVersionParseError;
106
107 fn from_str(value: &str) -> Result<Self, Self::Err> {
108 if let Some(value) = value.strip_prefix(">=") {
109 if value.is_empty() {
110 return Err(DependencyVersionParseError::VersionNotFound);
111 }
112 Ok(DependencyVersion {
113 constraint: DependencyConstraints::MoreOrEqualsThan,
114 version: value.to_owned(),
115 })
116 } else if let Some(value) = value.strip_prefix("<=") {
117 if value.is_empty() {
118 return Err(DependencyVersionParseError::VersionNotFound);
119 }
120 Ok(DependencyVersion {
121 constraint: DependencyConstraints::LessOrEqualsThan,
122 version: value.to_owned(),
123 })
124 } else if let Some(value) = value.strip_prefix('<') {
125 if value.is_empty() {
126 return Err(DependencyVersionParseError::VersionNotFound);
127 }
128 Ok(DependencyVersion {
129 constraint: DependencyConstraints::LessThan,
130 version: value.to_owned(),
131 })
132 } else if let Some(value) = value.strip_prefix('>') {
133 if value.is_empty() {
134 return Err(DependencyVersionParseError::VersionNotFound);
135 }
136 Ok(DependencyVersion {
137 constraint: DependencyConstraints::MoreThan,
138 version: value.to_owned(),
139 })
140 } else if let Some(value) = value.strip_prefix('=') {
141 if value.is_empty() {
142 return Err(DependencyVersionParseError::VersionNotFound);
143 }
144 Ok(DependencyVersion {
145 constraint: DependencyConstraints::Equals,
146 version: value.to_owned(),
147 })
148 } else {
149 Err(DependencyVersionParseError::ConstraintNotFound)
150 }
151 }
152}
153
154impl Display for DependencyVersion {
155 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
156 let dep = self.constraint.to_string() + &self.version;
157 f.write_str(&dep)
158 }
159}
160
161#[derive(Clone, Eq, PartialEq, Debug)]
162pub struct Dependency {
163 pub name: String,
165 pub version: Option<DependencyVersion>,
167}
168
169impl FromStr for Dependency {
170 type Err = DependencyVersionParseError;
171
172 fn from_str(value: &str) -> Result<Self, Self::Err> {
173 if let Some(pos) = value
174 .find('<')
175 .or_else(|| value.find('>'))
176 .or_else(|| value.find('='))
177 {
178 let version = DependencyVersion::from_str(&value[pos..])?;
179 Ok(Dependency {
180 name: value[..pos].to_owned(),
181 version: Some(version),
182 })
183 } else {
184 Ok(Dependency {
185 name: value.to_owned(),
186 version: None,
187 })
188 }
189 }
190}
191
192impl Display for Dependency {
193 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
194 if let Some(version) = self.version.as_ref() {
195 f.write_str(&self.name)?;
196 version.fmt(f)?;
197 } else {
198 f.write_str(&self.name)?;
199 }
200 Ok(())
201 }
202}
203
204impl<'de> Deserialize<'de> for Dependency {
205 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
206 where
207 D: Deserializer<'de>,
208 {
209 use serde::de::Error;
210 struct VisitorImpl;
211
212 impl<'de> Visitor<'de> for VisitorImpl {
213 type Value = Dependency;
214
215 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
216 write!(
217 formatter,
218 "dependency name(like 'test') with or without version constraint"
219 )
220 }
221
222 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
223 where
224 E: Error,
225 {
226 Dependency::from_str(v).map_err(|e| Error::custom(e.to_string()))
227 }
228 }
229
230 deserializer.deserialize_str(VisitorImpl)
231 }
232}
233
234impl Serialize for Dependency {
235 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
236 where
237 S: Serializer,
238 {
239 serializer.serialize_str(&self.to_string())
240 }
241}
242
243#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
245pub struct Package {
246 #[serde(rename = "FILENAME")]
248 pub file_name: String,
249 #[serde(rename = "NAME")]
251 pub name: String,
252 #[serde(rename = "BASE")]
254 pub base: Option<String>,
255 #[serde(rename = "VERSION")]
257 pub version: String,
258 #[serde(rename = "DESC")]
260 pub description: Option<String>,
261 #[serde(rename = "GROUPS")]
263 pub groups: Option<Vec<String>>,
264 #[serde(rename = "CSIZE")]
266 pub compressed_size: u64,
267 #[serde(rename = "ISIZE")]
269 pub installed_size: u64,
270 #[serde(rename = "MD5SUM")]
272 pub md5_sum: String,
273 #[serde(rename = "SHA256SUM")]
275 pub sha256_sum: String,
276 #[serde(rename = "PGPSIG")]
278 pub pgp_signature: String,
279 #[serde(rename = "URL")]
281 pub home_url: Option<String>,
282 #[serde(rename = "LICENSE")]
284 pub license: Option<Vec<String>>,
285 #[serde(rename = "ARCH")]
287 pub architecture: String,
288 #[serde(rename = "BUILDDATE", with = "date_serde")]
290 pub build_date: DateTime<Utc>,
291 #[serde(rename = "PACKAGER")]
293 pub packager: String,
294 #[serde(rename = "REPLACES")]
296 pub replaces: Option<Vec<String>>,
297 #[serde(rename = "CONFLICTS")]
299 pub conflicts: Option<Vec<String>>,
300 #[serde(rename = "PROVIDES")]
302 pub provides: Option<Vec<String>>,
303 #[serde(rename = "DEPENDS")]
305 pub depends: Option<Vec<Dependency>>,
306 #[serde(rename = "OPTDEPENDS")]
307 pub optdepends: Option<Vec<Dependency>>,
308 #[serde(rename = "MAKEDEPENDS")]
310 pub makedepends: Option<Vec<Dependency>>,
311 #[serde(rename = "CHECKDEPENDS")]
312 pub checkdepends: Option<Vec<Dependency>>,
313 #[serde(skip)]
322 pub linked_sources: Vec<Arc<Package>>,
323}
324
325impl Package {
326 pub fn base_package_for_csv(csv: &Package, suffix: &str) -> Self {
327 Package {
328 file_name: csv.file_name.clone(),
329 name: csv.name.replace(suffix, ""),
330 base: csv.base.clone().map(|name| name.replace(suffix, "")),
331 version: csv.version.replace(suffix, ""),
332 architecture: csv.architecture.clone(),
333 depends: csv.depends.clone(),
334 build_date: csv.build_date,
335 checkdepends: csv.checkdepends.clone(),
336 compressed_size: csv.compressed_size,
337 conflicts: csv.conflicts.clone(),
338 description: csv.description.clone(),
339 groups: csv.groups.clone(),
340 home_url: csv.home_url.clone(),
341 installed_size: csv.installed_size,
342 license: csv.license.clone(),
343 linked_sources: Vec::new(),
344 makedepends: csv.makedepends.clone(),
345 md5_sum: csv.md5_sum.clone(),
346 optdepends: csv.optdepends.clone(),
347 packager: csv.packager.clone(),
348 pgp_signature: csv.pgp_signature.clone(),
349 provides: csv.provides.clone(),
350 replaces: csv.replaces.clone(),
351 sha256_sum: csv.sha256_sum.clone(),
352 }
353 }
354}
355
356#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
357pub struct PackageFiles {
358 #[serde(rename = "FILES")]
359 pub files: Vec<String>,
360}
361
362mod date_serde {
363 use chrono::{DateTime, TimeZone, Utc};
364 use serde::{self, Deserialize, Deserializer, Serializer};
365
366 pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
367 where
368 S: Serializer,
369 {
370 serializer.serialize_i64(date.timestamp())
371 }
372
373 pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
374 where
375 D: Deserializer<'de>,
376 {
377 let timestamp = i64::deserialize(deserializer)?;
378 Ok(Utc.timestamp(timestamp, 0))
379 }
380}
381
382#[cfg(test)]
383mod test {
384 use crate::{Dependency, DependencyConstraints};
385 use std::str::FromStr;
386
387 #[test]
388 fn parse_dependency_version_constraint_more() {
389 let dep = Dependency::from_str("test>1.0").unwrap();
390 assert_eq!("test", dep.name);
391 let ver = dep.version.as_ref().unwrap();
392 assert_eq!("1.0", ver.version);
393 assert_eq!(DependencyConstraints::MoreThan, ver.constraint);
394 }
395
396 #[test]
397 fn parse_dependency_version_constraint_less() {
398 let dep = Dependency::from_str("test<1.0").unwrap();
399 assert_eq!("test", dep.name);
400 let ver = dep.version.as_ref().unwrap();
401 assert_eq!("1.0", ver.version);
402 assert_eq!(DependencyConstraints::LessThan, ver.constraint);
403 }
404
405 #[test]
406 fn parse_dependency_version_constraint_more_or_equals() {
407 let dep = Dependency::from_str("test>=1.0").unwrap();
408 assert_eq!("test", dep.name);
409 let ver = dep.version.as_ref().unwrap();
410 assert_eq!("1.0", ver.version);
411 assert_eq!(DependencyConstraints::MoreOrEqualsThan, ver.constraint);
412 }
413
414 #[test]
415 fn parse_dependency_version_constraint_less_or_equals() {
416 let dep = Dependency::from_str("test<=1.0").unwrap();
417 assert_eq!("test", dep.name);
418 let ver = dep.version.as_ref().unwrap();
419 assert_eq!("1.0", ver.version);
420 assert_eq!(DependencyConstraints::LessOrEqualsThan, ver.constraint);
421 }
422
423 #[test]
424 fn parse_dependency_version_constraint_equals() {
425 let dep = Dependency::from_str("test=1.0").unwrap();
426 assert_eq!("test", dep.name);
427 let ver = dep.version.as_ref().unwrap();
428 assert_eq!("1.0", ver.version);
429 assert_eq!(DependencyConstraints::Equals, ver.constraint);
430 }
431}