alpm_srcinfo/source_info/v1/
writer.rs

1//! Write implementation for [`SourceInfo`].
2
3use alpm_types::{Architecture, Architectures};
4
5use super::{
6    package::{Override, Package, PackageArchitecture},
7    package_base::{PackageBase, PackageBaseArchitecture},
8};
9#[cfg(doc)]
10use crate::SourceInfo;
11
12/// Pushes a section header to a [`String`].
13///
14/// Section headers are either `pkgname` or `pkgbase` and are **not** indented.
15fn push_section(section: &str, value: &str, output: &mut String) {
16    output.push_str(section);
17    output.push_str(" = ");
18    output.push_str(value);
19    output.push('\n');
20}
21
22/// Pushes a key-value pair to a [`String`].
23///
24/// Key-value pairs are scoped to a section.
25/// To make this visually distinguishable, the key-value pair is indented by a tab.
26fn push_key_value(key: &str, value: &str, output: &mut String) {
27    output.push('\t');
28    output.push_str(key);
29    output.push_str(" = ");
30    output.push_str(value);
31    output.push('\n');
32}
33
34/// Pushes a key-value pair to a [`String`], if it is set.
35///
36/// Key-value pairs are scoped to a section.
37/// To make this visually distinguishable, the key-value pair is indented by a tab.
38fn push_optional_value<T: ToString>(key: &str, value: &Option<T>, output: &mut String) {
39    let Some(value) = value else {
40        return;
41    };
42
43    push_key_value(key, &value.to_string(), output);
44}
45
46/// Pushes a list of key-value pairs in [SRCINFO] format to a [`String`].
47///
48/// Each value in `values` is added as a new line.
49/// If `values` is empty, nothing is added.
50///
51/// The Key-value pairs are fields scoped to a section.
52/// To make this visually distinguishable, each key-value pair is indented by a tab.
53///
54/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
55fn push_value_list<T: ToString>(key: &str, values: &Vec<T>, output: &mut String) {
56    for value in values {
57        push_key_value(key, &value.to_string(), output);
58    }
59}
60
61/// Appends a [`PackageBase`] in [SRCINFO] format to a [`String`].
62///
63/// The items in the `pkgbase` section are written to `output` in an order compatible with
64/// [makepkg].
65///
66/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
67/// [makepkg]: https://man.archlinux.org/man/makepkg.8
68pub(crate) fn pkgbase_section(base: &PackageBase, output: &mut String) {
69    push_section("pkgbase", base.name.inner(), output);
70
71    if let Some(description) = &base.description {
72        push_key_value("pkgdesc", description.as_ref(), output);
73    }
74    push_key_value("pkgver", &base.version.pkgver.to_string(), output);
75    push_key_value("pkgrel", &base.version.pkgrel.to_string(), output);
76    push_optional_value("epoch", &base.version.epoch, output);
77    push_optional_value("url", &base.url, output);
78    push_optional_value("install", &base.install, output);
79    push_optional_value("changelog", &base.changelog, output);
80    let architectures: Vec<Architecture> = (&base.architectures).into();
81    push_value_list("arch", &architectures, output);
82
83    push_value_list("groups", &base.groups, output);
84    push_value_list("license", &base.licenses, output);
85    push_value_list("checkdepends", &base.check_dependencies, output);
86    push_value_list("makedepends", &base.make_dependencies, output);
87    push_value_list("depends", &base.dependencies, output);
88    push_value_list("optdepends", &base.optional_dependencies, output);
89    push_value_list("provides", &base.provides, output);
90    push_value_list("conflicts", &base.conflicts, output);
91    push_value_list("replaces", &base.replaces, output);
92    push_value_list("noextract", &base.no_extracts, output);
93    push_value_list("options", &base.options, output);
94    push_value_list("backup", &base.backups, output);
95    push_value_list("source", &base.sources, output);
96    push_value_list("validpgpkeys", &base.pgp_fingerprints, output);
97    push_value_list("md5sums", &base.md5_checksums, output);
98    push_value_list("sha1sums", &base.sha1_checksums, output);
99    push_value_list("sha224sums", &base.sha224_checksums, output);
100    push_value_list("sha256sums", &base.sha256_checksums, output);
101    push_value_list("sha384sums", &base.sha384_checksums, output);
102    push_value_list("sha512sums", &base.sha512_checksums, output);
103    push_value_list("b2sums", &base.b2_checksums, output);
104    push_value_list("cksums", &base.crc_checksums, output);
105
106    // Go through architecture specific values **in the same order** as in `pkgbase.arch`.
107    // That's how `makepkg` does it.
108    for architecture in &base.architectures {
109        if let Architecture::Some(system_arch) = &architecture
110            && let Some(properties) = base.architecture_properties.get(system_arch)
111        {
112            pkgbase_architecture_properties(architecture, properties, output);
113        }
114    }
115}
116
117/// Appends a [`PackageBaseArchitecture`] based on an [`Architecture`] in [SRCINFO] format to a
118/// [`String`].
119///
120/// The architecture-specific `properties` are written to `output` in an order compatible with
121/// [makepkg].
122///
123/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
124/// [makepkg]: https://man.archlinux.org/man/makepkg.8
125fn pkgbase_architecture_properties(
126    architecture: Architecture,
127    properties: &PackageBaseArchitecture,
128    output: &mut String,
129) {
130    push_value_list(
131        &format!("source_{architecture}"),
132        &properties.sources,
133        output,
134    );
135    push_value_list(
136        &format!("provides_{architecture}"),
137        &properties.provides,
138        output,
139    );
140    push_value_list(
141        &format!("conflicts_{architecture}"),
142        &properties.conflicts,
143        output,
144    );
145    push_value_list(
146        &format!("depends_{architecture}"),
147        &properties.dependencies,
148        output,
149    );
150    push_value_list(
151        &format!("replaces_{architecture}"),
152        &properties.replaces,
153        output,
154    );
155    push_value_list(
156        &format!("optdepends_{architecture}"),
157        &properties.optional_dependencies,
158        output,
159    );
160    push_value_list(
161        &format!("makedepends_{architecture}"),
162        &properties.make_dependencies,
163        output,
164    );
165    push_value_list(
166        &format!("checkdepends_{architecture}"),
167        &properties.check_dependencies,
168        output,
169    );
170    push_value_list(
171        &format!("md5sums_{architecture}"),
172        &properties.md5_checksums,
173        output,
174    );
175    push_value_list(
176        &format!("sha1sums_{architecture}"),
177        &properties.sha1_checksums,
178        output,
179    );
180    push_value_list(
181        &format!("sha224sums_{architecture}"),
182        &properties.sha224_checksums,
183        output,
184    );
185    push_value_list(
186        &format!("sha256sums_{architecture}"),
187        &properties.sha256_checksums,
188        output,
189    );
190    push_value_list(
191        &format!("sha384sums_{architecture}"),
192        &properties.sha384_checksums,
193        output,
194    );
195    push_value_list(
196        &format!("sha512sums_{architecture}"),
197        &properties.sha512_checksums,
198        output,
199    );
200    push_value_list(
201        &format!("b2sums_{architecture}"),
202        &properties.b2_checksums,
203        output,
204    );
205    push_value_list(
206        &format!("cksums_{architecture}"),
207        &properties.crc_checksums,
208        output,
209    )
210}
211
212/// Pushes an override key-value pair in [SRCINFO] format to a [`String`].
213///
214/// Key-value pairs are scoped to a section.
215/// To make this visually distinguishable, the key-value pair is indented by a tab.
216///
217/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
218fn push_override_value<T: ToString>(key: &str, value: &Override<T>, output: &mut String) {
219    match value {
220        Override::No => (),
221        Override::Clear => {
222            // Clear the value
223            output.push('\t');
224            output.push_str(key);
225            output.push_str(" = \n");
226        }
227        Override::Yes { value } => {
228            push_key_value(key, &value.to_string(), output);
229        }
230    }
231}
232
233/// Pushes a list of override key-value pairs in [SRCINFO] format to a [`String`].
234///
235/// Each value in `values` is added as a new line.
236/// If `values` is empty, nothing is added.
237///
238/// The Key-value pairs are fields scoped to a section.
239/// To make this visually distinguishable, each key-value pair is indented by a tab.
240///
241/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
242fn push_override_value_list<T: ToString>(
243    key: &str,
244    values: &Override<Vec<T>>,
245    output: &mut String,
246) {
247    match values {
248        Override::No => (),
249        Override::Clear => {
250            // Clear the value
251            output.push('\t');
252            output.push_str(key);
253            output.push_str(" = \n");
254        }
255        Override::Yes { value } => {
256            for inner_value in value {
257                push_key_value(key, &inner_value.to_string(), output);
258            }
259        }
260    }
261}
262
263/// Appends a [`Package`] with an [`Architecture`] in [SRCINFO] format to a [`String`].
264///
265/// The items in the `pkgname` section are written to `output` in an order compatible with
266/// [makepkg].
267///
268/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
269/// [makepkg]: https://man.archlinux.org/man/makepkg.8
270pub(crate) fn pkgname_section(
271    package: &Package,
272    base_architectures: &Architectures,
273    output: &mut String,
274) {
275    push_section("pkgname", package.name.inner(), output);
276
277    push_override_value("pkgdesc", &package.description, output);
278    push_override_value("url", &package.url, output);
279    push_override_value("install", &package.install, output);
280    push_override_value("changelog", &package.changelog, output);
281
282    if let Some(architectures) = &package.architectures {
283        let arch_vec: Vec<Architecture> = architectures.into();
284        push_value_list("arch", &arch_vec, output);
285    }
286
287    push_override_value_list("groups", &package.groups, output);
288    push_override_value_list("license", &package.licenses, output);
289    push_override_value_list("depends", &package.dependencies, output);
290    push_override_value_list("optdepends", &package.optional_dependencies, output);
291    push_override_value_list("provides", &package.provides, output);
292    push_override_value_list("conflicts", &package.conflicts, output);
293    push_override_value_list("replaces", &package.replaces, output);
294    push_override_value_list("options", &package.options, output);
295    push_override_value_list("backup", &package.backups, output);
296
297    // Go through architecture specific values **in the same order** as in `pkgbase.arch`.
298    for architecture in base_architectures {
299        if let Architecture::Some(system_arch) = &architecture
300            && let Some(properties) = package.architecture_properties.get(system_arch)
301        {
302            pkgname_architecture_properties(architecture, properties, output);
303        }
304    }
305}
306
307/// Appends a [`PackageArchitecture`] based on an [`Architecture`] in [SRCINFO] format to a
308/// [`String`].
309///
310/// The architecture-specific `properties` are written to `output` in an order compatible with
311/// [makepkg].
312///
313/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
314/// [makepkg]: https://man.archlinux.org/man/makepkg.8
315fn pkgname_architecture_properties(
316    architecture: Architecture,
317    properties: &PackageArchitecture,
318    output: &mut String,
319) {
320    push_override_value_list(
321        &format!("provides_{architecture}"),
322        &properties.provides,
323        output,
324    );
325    push_override_value_list(
326        &format!("conflicts_{architecture}"),
327        &properties.conflicts,
328        output,
329    );
330    push_override_value_list(
331        &format!("depends_{architecture}"),
332        &properties.dependencies,
333        output,
334    );
335    push_override_value_list(
336        &format!("replaces_{architecture}"),
337        &properties.replaces,
338        output,
339    );
340    push_override_value_list(
341        &format!("optdepends_{architecture}"),
342        &properties.optional_dependencies,
343        output,
344    );
345}