debian_packaging/
binary_package_control.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5/*! Debian binary package control files. */
6
7use {
8    crate::{
9        control::ControlParagraph,
10        dependency::{DependencyList, PackageDependencyFields},
11        error::{DebianError, Result},
12        io::ContentDigest,
13        package_version::PackageVersion,
14        repository::{builder::DebPackageReference, release::ChecksumType},
15    },
16    std::ops::{Deref, DerefMut},
17};
18
19/// A Debian binary package control file/paragraph.
20///
21/// See <https://www.debian.org/doc/debian-policy/ch-controlfields.html#binary-package-control-files-debian-control>.
22///
23/// Binary package control files are defined by a single paragraph with well-defined
24/// fields. This type is a low-level wrapper around an inner [ControlParagraph].
25/// [Deref] and [DerefMut] can be used to operate on the inner [ControlParagraph].
26/// [From] and [Into] are implemented in both directions to enable cheap coercion
27/// between the types.
28///
29/// Binary package control paragraphs are seen in `DEBIAN/control` files. Variations
30/// also exist in `Packages` files in repositories and elsewhere.
31///
32/// Fields annotated as *mandatory* in the Debian Policy Manual have getters that
33/// return [Result] and will error if a field is not present. Non-mandatory fields
34/// return [Option]. This enforcement can be bypassed by calling
35/// [ControlParagraph::field()].
36#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
37pub struct BinaryPackageControlFile<'a> {
38    paragraph: ControlParagraph<'a>,
39}
40
41impl<'a> Deref for BinaryPackageControlFile<'a> {
42    type Target = ControlParagraph<'a>;
43
44    fn deref(&self) -> &Self::Target {
45        &self.paragraph
46    }
47}
48
49impl<'a> DerefMut for BinaryPackageControlFile<'a> {
50    fn deref_mut(&mut self) -> &mut Self::Target {
51        &mut self.paragraph
52    }
53}
54
55impl<'a> From<ControlParagraph<'a>> for BinaryPackageControlFile<'a> {
56    fn from(paragraph: ControlParagraph<'a>) -> Self {
57        Self { paragraph }
58    }
59}
60
61impl<'a> From<BinaryPackageControlFile<'a>> for ControlParagraph<'a> {
62    fn from(cf: BinaryPackageControlFile<'a>) -> Self {
63        cf.paragraph
64    }
65}
66
67impl<'a> BinaryPackageControlFile<'a> {
68    /// The `Package` field value.
69    pub fn package(&self) -> Result<&str> {
70        self.required_field_str("Package")
71    }
72
73    /// The `Version` field as its original string.
74    pub fn version_str(&self) -> Result<&str> {
75        self.required_field_str("Version")
76    }
77
78    /// The `Version` field parsed into a [PackageVersion].
79    pub fn version(&self) -> Result<PackageVersion> {
80        PackageVersion::parse(self.version_str()?)
81    }
82
83    /// The `Architecture` field.
84    pub fn architecture(&self) -> Result<&str> {
85        self.required_field_str("Architecture")
86    }
87
88    /// The `Maintainer` field.
89    pub fn maintainer(&self) -> Result<&str> {
90        self.required_field_str("Maintainer")
91    }
92
93    /// The `Description` field.
94    pub fn description(&self) -> Result<&str> {
95        self.required_field_str("Description")
96    }
97
98    /// The `Source` field.
99    pub fn source(&self) -> Option<&str> {
100        self.field_str("Source")
101    }
102
103    /// The `Section` field.
104    pub fn section(&self) -> Option<&str> {
105        self.field_str("Section")
106    }
107
108    /// The `Priority` field.
109    pub fn priority(&self) -> Option<&str> {
110        self.field_str("Priority")
111    }
112
113    /// The `Essential` field.
114    pub fn essential(&self) -> Option<&str> {
115        self.field_str("Essential")
116    }
117
118    /// The `Homepage` field.
119    pub fn homepage(&self) -> Option<&str> {
120        self.field_str("Homepage")
121    }
122
123    /// The `Installed-Size` field, parsed to a [u64].
124    pub fn installed_size(&self) -> Option<Result<u64>> {
125        self.field_u64("Installed-Size")
126    }
127
128    /// The `Size` field, parsed to a [u64].
129    pub fn size(&self) -> Option<Result<u64>> {
130        self.field_u64("Size")
131    }
132
133    /// The `Built-Using` field.
134    pub fn built_using(&self) -> Option<&str> {
135        self.field_str("Built-Using")
136    }
137
138    /// The `Depends` field, parsed to a [DependencyList].
139    pub fn depends(&self) -> Option<Result<DependencyList>> {
140        self.field_dependency_list("Depends")
141    }
142
143    /// The `Recommends` field, parsed to a [DependencyList].
144    pub fn recommends(&self) -> Option<Result<DependencyList>> {
145        self.field_dependency_list("Recommends")
146    }
147
148    /// The `Suggests` field, parsed to a [DependencyList].
149    pub fn suggests(&self) -> Option<Result<DependencyList>> {
150        self.field_dependency_list("Suggests")
151    }
152
153    /// The `Enhances` field, parsed to a [DependencyList].
154    pub fn enhances(&self) -> Option<Result<DependencyList>> {
155        self.field_dependency_list("Enhances")
156    }
157
158    /// The `Pre-Depends` field, parsed to a [DependencyList].
159    pub fn pre_depends(&self) -> Option<Result<DependencyList>> {
160        self.field_dependency_list("Pre-Depends")
161    }
162
163    /// Obtain parsed values of all fields defining dependencies.
164    pub fn package_dependency_fields(&self) -> Result<PackageDependencyFields> {
165        PackageDependencyFields::from_paragraph(self)
166    }
167}
168
169impl<'cf, 'a: 'cf> DebPackageReference<'cf> for BinaryPackageControlFile<'a> {
170    fn deb_size_bytes(&self) -> Result<u64> {
171        self.size()
172            .ok_or_else(|| DebianError::ControlRequiredFieldMissing("Size".to_string()))?
173    }
174
175    fn deb_digest(&self, checksum: ChecksumType) -> Result<ContentDigest> {
176        let hex_digest = self
177            .paragraph
178            .field_str(checksum.field_name())
179            .ok_or_else(|| {
180                DebianError::ControlRequiredFieldMissing(checksum.field_name().to_string())
181            })?;
182
183        ContentDigest::from_hex_digest(checksum, hex_digest)
184    }
185
186    fn deb_filename(&self) -> Result<String> {
187        let filename = self
188            .field_str("Filename")
189            .ok_or_else(|| DebianError::ControlRequiredFieldMissing("Filename".to_string()))?;
190
191        Ok(if let Some((_, s)) = filename.rsplit_once('/') {
192            s.to_string()
193        } else {
194            filename.to_string()
195        })
196    }
197
198    fn control_file_for_packages_index(&self) -> Result<BinaryPackageControlFile<'cf>> {
199        Ok(self.clone())
200    }
201}