debian_control/
fields.rs

1//! Fields for the control file
2use std::str::FromStr;
3
4/// Priority of a package
5#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
6pub enum Priority {
7    /// Required
8    Required,
9
10    /// Important
11    Important,
12
13    /// Standard
14    Standard,
15
16    /// Optional
17    Optional,
18
19    /// Extra
20    Extra,
21}
22
23impl std::fmt::Display for Priority {
24    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
25        f.write_str(match self {
26            Priority::Required => "required",
27            Priority::Important => "important",
28            Priority::Standard => "standard",
29            Priority::Optional => "optional",
30            Priority::Extra => "extra",
31        })
32    }
33}
34
35impl std::str::FromStr for Priority {
36    type Err = String;
37
38    fn from_str(s: &str) -> Result<Self, Self::Err> {
39        match s {
40            "required" => Ok(Priority::Required),
41            "important" => Ok(Priority::Important),
42            "standard" => Ok(Priority::Standard),
43            "optional" => Ok(Priority::Optional),
44            "extra" => Ok(Priority::Extra),
45            _ => Err(format!("Invalid priority: {}", s)),
46        }
47    }
48}
49
50/// A checksum of a file
51pub trait Checksum {
52    /// Filename
53    fn filename(&self) -> &str;
54
55    /// Size of the file, in bytes
56    fn size(&self) -> usize;
57}
58
59/// SHA1 checksum
60#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
61pub struct Sha1Checksum {
62    /// SHA1 checksum
63    pub sha1: String,
64
65    /// Size of the file, in bytes
66    pub size: usize,
67
68    /// Filename
69    pub filename: String,
70}
71
72impl Checksum for Sha1Checksum {
73    fn filename(&self) -> &str {
74        &self.filename
75    }
76
77    fn size(&self) -> usize {
78        self.size
79    }
80}
81
82impl std::fmt::Display for Sha1Checksum {
83    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
84        write!(f, "{} {} {}", self.sha1, self.size, self.filename)
85    }
86}
87
88impl std::str::FromStr for Sha1Checksum {
89    type Err = String;
90
91    fn from_str(s: &str) -> Result<Self, Self::Err> {
92        let mut parts = s.split_whitespace();
93        let sha1 = parts.next().ok_or_else(|| "Missing sha1".to_string())?;
94        let size = parts
95            .next()
96            .ok_or_else(|| "Missing size".to_string())?
97            .parse()
98            .map_err(|e: std::num::ParseIntError| e.to_string())?;
99        let filename = parts
100            .next()
101            .ok_or_else(|| "Missing filename".to_string())?
102            .to_string();
103        Ok(Self {
104            sha1: sha1.to_string(),
105            size,
106            filename,
107        })
108    }
109}
110
111/// SHA-256 checksum
112#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
113pub struct Sha256Checksum {
114    /// SHA-256 checksum
115    pub sha256: String,
116
117    /// Size of the file, in bytes
118    pub size: usize,
119
120    /// Filename
121    pub filename: String,
122}
123
124impl Checksum for Sha256Checksum {
125    fn filename(&self) -> &str {
126        &self.filename
127    }
128
129    fn size(&self) -> usize {
130        self.size
131    }
132}
133
134impl std::fmt::Display for Sha256Checksum {
135    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
136        write!(f, "{} {} {}", self.sha256, self.size, self.filename)
137    }
138}
139
140impl std::str::FromStr for Sha256Checksum {
141    type Err = String;
142
143    fn from_str(s: &str) -> Result<Self, Self::Err> {
144        let mut parts = s.split_whitespace();
145        let sha256 = parts.next().ok_or_else(|| "Missing sha256".to_string())?;
146        let size = parts
147            .next()
148            .ok_or_else(|| "Missing size".to_string())?
149            .parse()
150            .map_err(|e: std::num::ParseIntError| e.to_string())?;
151        let filename = parts
152            .next()
153            .ok_or_else(|| "Missing filename".to_string())?
154            .to_string();
155        Ok(Self {
156            sha256: sha256.to_string(),
157            size,
158            filename,
159        })
160    }
161}
162
163/// SHA-512 checksum
164#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
165pub struct Sha512Checksum {
166    /// SHA-512 checksum
167    pub sha512: String,
168
169    /// Size of the file, in bytes
170    pub size: usize,
171
172    /// Filename
173    pub filename: String,
174}
175
176impl Checksum for Sha512Checksum {
177    fn filename(&self) -> &str {
178        &self.filename
179    }
180
181    fn size(&self) -> usize {
182        self.size
183    }
184}
185
186impl std::fmt::Display for Sha512Checksum {
187    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
188        write!(f, "{} {} {}", self.sha512, self.size, self.filename)
189    }
190}
191
192impl std::str::FromStr for Sha512Checksum {
193    type Err = String;
194
195    fn from_str(s: &str) -> Result<Self, Self::Err> {
196        let mut parts = s.split_whitespace();
197        let sha512 = parts.next().ok_or_else(|| "Missing sha512".to_string())?;
198        let size = parts
199            .next()
200            .ok_or_else(|| "Missing size".to_string())?
201            .parse()
202            .map_err(|e: std::num::ParseIntError| e.to_string())?;
203        let filename = parts
204            .next()
205            .ok_or_else(|| "Missing filename".to_string())?
206            .to_string();
207        Ok(Self {
208            sha512: sha512.to_string(),
209            size,
210            filename,
211        })
212    }
213}
214
215/// An MD5 checksum of a file
216#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
217pub struct Md5Checksum {
218    /// The MD5 checksum
219    pub md5sum: String,
220    /// The size of the file
221    pub size: usize,
222    /// The filename
223    pub filename: String,
224}
225
226impl std::fmt::Display for Md5Checksum {
227    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
228        write!(f, "{} {} {}", self.md5sum, self.size, self.filename)
229    }
230}
231
232impl std::str::FromStr for Md5Checksum {
233    type Err = ();
234
235    fn from_str(s: &str) -> Result<Self, Self::Err> {
236        let mut parts = s.split_whitespace();
237        let md5sum = parts.next().ok_or(())?;
238        let size = parts.next().ok_or(())?.parse().map_err(|_| ())?;
239        let filename = parts.next().ok_or(())?.to_string();
240        Ok(Self {
241            md5sum: md5sum.to_string(),
242            size,
243            filename,
244        })
245    }
246}
247
248impl Checksum for Md5Checksum {
249    fn filename(&self) -> &str {
250        &self.filename
251    }
252
253    fn size(&self) -> usize {
254        self.size
255    }
256}
257
258/// A package list entry
259#[derive(Debug, Clone, PartialEq, Eq)]
260pub struct PackageListEntry {
261    /// Package name
262    pub package: String,
263
264    /// Package type
265    pub package_type: String,
266
267    /// Section
268    pub section: String,
269
270    /// Priority
271    pub priority: Priority,
272
273    /// Extra fields
274    pub extra: std::collections::HashMap<String, String>,
275}
276
277impl PackageListEntry {
278    /// Create a new package list entry
279    pub fn new(package: &str, package_type: &str, section: &str, priority: Priority) -> Self {
280        Self {
281            package: package.to_string(),
282            package_type: package_type.to_string(),
283            section: section.to_string(),
284            priority,
285            extra: std::collections::HashMap::new(),
286        }
287    }
288}
289
290impl std::fmt::Display for PackageListEntry {
291    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
292        write!(
293            f,
294            "{} {} {} {}",
295            self.package, self.package_type, self.section, self.priority
296        )?;
297        for (k, v) in &self.extra {
298            write!(f, " {}={}", k, v)?;
299        }
300        Ok(())
301    }
302}
303
304impl std::str::FromStr for PackageListEntry {
305    type Err = String;
306
307    fn from_str(s: &str) -> Result<Self, Self::Err> {
308        let mut parts = s.split_whitespace();
309        let package = parts
310            .next()
311            .ok_or_else(|| "Missing package".to_string())?
312            .to_string();
313        let package_type = parts
314            .next()
315            .ok_or_else(|| "Missing package type".to_string())?
316            .to_string();
317        let section = parts
318            .next()
319            .ok_or_else(|| "Missing section".to_string())?
320            .to_string();
321        let priority = parts
322            .next()
323            .ok_or_else(|| "Missing priority".to_string())?
324            .parse()?;
325        let mut extra = std::collections::HashMap::new();
326        for part in parts {
327            let mut kv = part.split('=');
328            let k = kv
329                .next()
330                .ok_or_else(|| "Missing key".to_string())?
331                .to_string();
332            let v = kv
333                .next()
334                .ok_or_else(|| "Missing value".to_string())?
335                .to_string();
336            extra.insert(k, v);
337        }
338        Ok(Self {
339            package,
340            package_type,
341            section,
342            priority,
343            extra,
344        })
345    }
346}
347
348/// Urgency of a particular package version
349#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
350pub enum Urgency {
351    /// Low
352    #[default]
353    Low,
354    /// Medium
355    Medium,
356    /// High
357    High,
358    /// Emergency
359    Emergency,
360    /// Critical
361    Critical,
362}
363
364impl std::fmt::Display for Urgency {
365    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
366        match self {
367            Urgency::Low => f.write_str("low"),
368            Urgency::Medium => f.write_str("medium"),
369            Urgency::High => f.write_str("high"),
370            Urgency::Emergency => f.write_str("emergency"),
371            Urgency::Critical => f.write_str("critical"),
372        }
373    }
374}
375
376impl FromStr for Urgency {
377    type Err = String;
378
379    fn from_str(s: &str) -> Result<Self, Self::Err> {
380        match s.to_lowercase().as_str() {
381            "low" => Ok(Urgency::Low),
382            "medium" => Ok(Urgency::Medium),
383            "high" => Ok(Urgency::High),
384            "emergency" => Ok(Urgency::Emergency),
385            "critical" => Ok(Urgency::Critical),
386            _ => Err(format!("invalid urgency: {}", s)),
387        }
388    }
389}
390
391/// Multi-arch policy
392#[derive(PartialEq, Eq, Debug, Default)]
393pub enum MultiArch {
394    /// Indicates that the package is identical across all architectures. The package can satisfy dependencies for other architectures.
395    Same,
396    /// The package can be installed alongside the same package of other architectures. It doesn't provide files that conflict with other architectures.
397    Foreign,
398    /// The package is only for its native architecture and cannot satisfy dependencies for other architectures.
399    #[default]
400    No,
401    /// Similar to "foreign", but the package manager may choose not to install it for foreign architectures if a native package is available.
402    Allowed,
403}
404
405impl std::str::FromStr for MultiArch {
406    type Err = String;
407
408    fn from_str(s: &str) -> Result<Self, Self::Err> {
409        match s {
410            "same" => Ok(MultiArch::Same),
411            "foreign" => Ok(MultiArch::Foreign),
412            "no" => Ok(MultiArch::No),
413            "allowed" => Ok(MultiArch::Allowed),
414            _ => Err(format!("Invalid multiarch: {}", s)),
415        }
416    }
417}
418
419impl std::fmt::Display for MultiArch {
420    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
421        f.write_str(match self {
422            MultiArch::Same => "same",
423            MultiArch::Foreign => "foreign",
424            MultiArch::No => "no",
425            MultiArch::Allowed => "allowed",
426        })
427    }
428}
429
430#[cfg(test)]
431mod tests {
432    use super::*;
433
434    #[test]
435    fn test_sha1_checksum_filename() {
436        let checksum = Sha1Checksum {
437            sha1: "abc123".to_string(),
438            size: 1234,
439            filename: "test.deb".to_string(),
440        };
441        assert_eq!(checksum.filename(), "test.deb".to_string());
442    }
443
444    #[test]
445    fn test_md5_checksum_filename() {
446        let checksum = Md5Checksum {
447            md5sum: "abc123".to_string(),
448            size: 1234,
449            filename: "test.deb".to_string(),
450        };
451        assert_eq!(checksum.filename(), "test.deb".to_string());
452    }
453
454    #[test]
455    fn test_sha256_checksum_filename() {
456        let checksum = Sha256Checksum {
457            sha256: "abc123".to_string(),
458            size: 1234,
459            filename: "test.deb".to_string(),
460        };
461        assert_eq!(checksum.filename(), "test.deb".to_string());
462    }
463
464    #[test]
465    fn test_sha512_checksum_filename() {
466        let checksum = Sha512Checksum {
467            sha512: "abc123".to_string(),
468            size: 1234,
469            filename: "test.deb".to_string(),
470        };
471        assert_eq!(checksum.filename(), "test.deb".to_string());
472    }
473}