archlinux_repo/
data.rs

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    /// <
36    LessThan,
37    /// >
38    MoreThan,
39    /// =
40    Equals,
41    /// >=
42    MoreOrEqualsThan,
43    /// <=
44    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    /// dependency name
164    pub name: String,
165    /// dependency version constraint. If None - match all dependencies with given name
166    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/// Repository package
244#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
245pub struct Package {
246    /// file name
247    #[serde(rename = "FILENAME")]
248    pub file_name: String,
249    /// name
250    #[serde(rename = "NAME")]
251    pub name: String,
252    /// name without architecture
253    #[serde(rename = "BASE")]
254    pub base: Option<String>,
255    /// version
256    #[serde(rename = "VERSION")]
257    pub version: String,
258    /// description
259    #[serde(rename = "DESC")]
260    pub description: Option<String>,
261    /// package groups
262    #[serde(rename = "GROUPS")]
263    pub groups: Option<Vec<String>>,
264    /// tar.xz archive size
265    #[serde(rename = "CSIZE")]
266    pub compressed_size: u64,
267    /// installed files size
268    #[serde(rename = "ISIZE")]
269    pub installed_size: u64,
270    /// MD5 checksum
271    #[serde(rename = "MD5SUM")]
272    pub md5_sum: String,
273    /// SHA256 checksum
274    #[serde(rename = "SHA256SUM")]
275    pub sha256_sum: String,
276    /// PGP signature
277    #[serde(rename = "PGPSIG")]
278    pub pgp_signature: String,
279    /// package home url
280    #[serde(rename = "URL")]
281    pub home_url: Option<String>,
282    /// license name
283    #[serde(rename = "LICENSE")]
284    pub license: Option<Vec<String>>,
285    /// processor architecture
286    #[serde(rename = "ARCH")]
287    pub architecture: String,
288    /// build date
289    #[serde(rename = "BUILDDATE", with = "date_serde")]
290    pub build_date: DateTime<Utc>,
291    /// who created this package
292    #[serde(rename = "PACKAGER")]
293    pub packager: String,
294    /// packages which this package replaces
295    #[serde(rename = "REPLACES")]
296    pub replaces: Option<Vec<String>>,
297    /// packages which cannot be used with this package
298    #[serde(rename = "CONFLICTS")]
299    pub conflicts: Option<Vec<String>>,
300    /// packages provided by this package
301    #[serde(rename = "PROVIDES")]
302    pub provides: Option<Vec<String>>,
303    /// run-time dependencies
304    #[serde(rename = "DEPENDS")]
305    pub depends: Option<Vec<Dependency>>,
306    #[serde(rename = "OPTDEPENDS")]
307    pub optdepends: Option<Vec<Dependency>>,
308    /// build-time dependencies
309    #[serde(rename = "MAKEDEPENDS")]
310    pub makedepends: Option<Vec<Dependency>>,
311    #[serde(rename = "CHECKDEPENDS")]
312    pub checkdepends: Option<Vec<Dependency>>,
313    /// VCS packages with same name. For example `test-git-1.0` is a VCS package for `test-1.0`.
314    /// Supported prefixes are:
315    /// - cvs
316    /// - svn
317    /// - hg
318    /// - darcs
319    /// - bzr
320    /// - git
321    #[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}