alpm_srcinfo/source_info/v1/
writer.rs

1//! Write implementation for [`SourceInfo`].
2
3use alpm_types::Architecture;
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.package_version.to_string(), output);
75    push_key_value("pkgrel", &base.package_release.to_string(), output);
76    push_optional_value("epoch", &base.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    push_value_list("arch", &base.architectures, output);
81
82    push_value_list("groups", &base.groups, output);
83    push_value_list("license", &base.licenses, output);
84    push_value_list("checkdepends", &base.check_dependencies, output);
85    push_value_list("makedepends", &base.make_dependencies, output);
86    push_value_list("depends", &base.dependencies, output);
87    push_value_list("optdepends", &base.optional_dependencies, output);
88    push_value_list("provides", &base.provides, output);
89    push_value_list("conflicts", &base.conflicts, output);
90    push_value_list("replaces", &base.replaces, output);
91    push_value_list("noextract", &base.no_extracts, output);
92    push_value_list("options", &base.options, output);
93    push_value_list("backup", &base.backups, output);
94    push_value_list("source", &base.sources, output);
95    push_value_list("validpgpkeys", &base.pgp_fingerprints, output);
96    push_value_list("md5sums", &base.md5_checksums, output);
97    push_value_list("sha1sums", &base.sha1_checksums, output);
98    push_value_list("sha224sums", &base.sha224_checksums, output);
99    push_value_list("sha256sums", &base.sha256_checksums, output);
100    push_value_list("sha384sums", &base.sha384_checksums, output);
101    push_value_list("sha512sums", &base.sha512_checksums, output);
102    push_value_list("b2sums", &base.b2_checksums, output);
103
104    // Go through architecture specific values **in the same order** as in `pkgbase.arch`.
105    // That's how `makepkg` does it.
106    for architecture in &base.architectures {
107        if let Some(properties) = base.architecture_properties.get(architecture) {
108            pkgbase_architecture_properties(*architecture, properties, output);
109        }
110    }
111}
112
113/// Appends a [`PackageBaseArchitecture`] based on an [`Architecture`] in [SRCINFO] format to a
114/// [`String`].
115///
116/// The architecture-specific `properties` are written to `output` in an order compatible with
117/// [makepkg].
118///
119/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
120/// [makepkg]: https://man.archlinux.org/man/makepkg.8
121fn pkgbase_architecture_properties(
122    architecture: Architecture,
123    properties: &PackageBaseArchitecture,
124    output: &mut String,
125) {
126    push_value_list(
127        &format!("source_{architecture}"),
128        &properties.sources,
129        output,
130    );
131    push_value_list(
132        &format!("provides_{architecture}"),
133        &properties.provides,
134        output,
135    );
136    push_value_list(
137        &format!("conflicts_{architecture}"),
138        &properties.conflicts,
139        output,
140    );
141    push_value_list(
142        &format!("depends_{architecture}"),
143        &properties.dependencies,
144        output,
145    );
146    push_value_list(
147        &format!("replaces_{architecture}"),
148        &properties.replaces,
149        output,
150    );
151    push_value_list(
152        &format!("optdepends_{architecture}"),
153        &properties.optional_dependencies,
154        output,
155    );
156    push_value_list(
157        &format!("makedepends_{architecture}"),
158        &properties.make_dependencies,
159        output,
160    );
161    push_value_list(
162        &format!("checkdepends_{architecture}"),
163        &properties.check_dependencies,
164        output,
165    );
166    push_value_list(
167        &format!("md5sums_{architecture}"),
168        &properties.md5_checksums,
169        output,
170    );
171    push_value_list(
172        &format!("sha1sums_{architecture}"),
173        &properties.sha1_checksums,
174        output,
175    );
176    push_value_list(
177        &format!("sha224sums_{architecture}"),
178        &properties.sha224_checksums,
179        output,
180    );
181    push_value_list(
182        &format!("sha256sums_{architecture}"),
183        &properties.sha256_checksums,
184        output,
185    );
186    push_value_list(
187        &format!("sha384sums_{architecture}"),
188        &properties.sha384_checksums,
189        output,
190    );
191    push_value_list(
192        &format!("sha512sums_{architecture}"),
193        &properties.sha512_checksums,
194        output,
195    );
196    push_value_list(
197        &format!("b2sums_{architecture}"),
198        &properties.b2_checksums,
199        output,
200    );
201}
202
203/// Pushes an override key-value pair in [SRCINFO] format to a [`String`].
204///
205/// Key-value pairs are scoped to a section.
206/// To make this visually distinguishable, the key-value pair is indented by a tab.
207///
208/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
209fn push_override_value<T: ToString>(key: &str, value: &Override<T>, output: &mut String) {
210    match value {
211        Override::No => (),
212        Override::Clear => {
213            // Clear the value
214            output.push('\t');
215            output.push_str(key);
216            output.push_str(" = \n");
217        }
218        Override::Yes { value } => {
219            push_key_value(key, &value.to_string(), output);
220        }
221    }
222}
223
224/// Pushes a list of override key-value pairs in [SRCINFO] format to a [`String`].
225///
226/// Each value in `values` is added as a new line.
227/// If `values` is empty, nothing is added.
228///
229/// The Key-value pairs are fields scoped to a section.
230/// To make this visually distinguishable, each key-value pair is indented by a tab.
231///
232/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
233fn push_override_value_list<T: ToString>(
234    key: &str,
235    values: &Override<Vec<T>>,
236    output: &mut String,
237) {
238    match values {
239        Override::No => (),
240        Override::Clear => {
241            // Clear the value
242            output.push('\t');
243            output.push_str(key);
244            output.push_str(" = \n");
245        }
246        Override::Yes { value } => {
247            for inner_value in value {
248                push_key_value(key, &inner_value.to_string(), output);
249            }
250        }
251    }
252}
253
254/// Appends a [`Package`] with an [`Architecture`] in [SRCINFO] format to a [`String`].
255///
256/// The items in the `pkgname` section are written to `output` in an order compatible with
257/// [makepkg].
258///
259/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
260/// [makepkg]: https://man.archlinux.org/man/makepkg.8
261pub(crate) fn pkgname_section(
262    package: &Package,
263    base_architectures: &[Architecture],
264    output: &mut String,
265) {
266    push_section("pkgname", package.name.inner(), output);
267
268    push_override_value("pkgdesc", &package.description, output);
269    push_override_value("url", &package.url, output);
270    push_override_value("install", &package.install, output);
271    push_override_value("changelog", &package.changelog, output);
272
273    if let Some(architectures) = &package.architectures {
274        push_value_list("arch", architectures, output);
275    }
276
277    push_override_value_list("groups", &package.groups, output);
278    push_override_value_list("license", &package.licenses, output);
279    push_override_value_list("depends", &package.dependencies, output);
280    push_override_value_list("optdepends", &package.optional_dependencies, output);
281    push_override_value_list("provides", &package.provides, output);
282    push_override_value_list("conflicts", &package.conflicts, output);
283    push_override_value_list("replaces", &package.replaces, output);
284    push_override_value_list("options", &package.options, output);
285    push_override_value_list("backup", &package.backups, output);
286
287    // Go through architecture specific values **in the same order** as in `pkgbase.arch`.
288    for architecture in base_architectures {
289        if let Some(properties) = package.architecture_properties.get(architecture) {
290            pkgname_architecture_properties(*architecture, properties, output);
291        }
292    }
293}
294
295/// Appends a [`PackageArchitecture`] based on an [`Architecture`] in [SRCINFO] format to a
296/// [`String`].
297///
298/// The architecture-specific `properties` are written to `output` in an order compatible with
299/// [makepkg].
300///
301/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
302/// [makepkg]: https://man.archlinux.org/man/makepkg.8
303fn pkgname_architecture_properties(
304    architecture: Architecture,
305    properties: &PackageArchitecture,
306    output: &mut String,
307) {
308    push_override_value_list(
309        &format!("provides_{architecture}"),
310        &properties.provides,
311        output,
312    );
313    push_override_value_list(
314        &format!("conflicts_{architecture}"),
315        &properties.conflicts,
316        output,
317    );
318    push_override_value_list(
319        &format!("depends_{architecture}"),
320        &properties.dependencies,
321        output,
322    );
323    push_override_value_list(
324        &format!("replaces_{architecture}"),
325        &properties.replaces,
326        output,
327    );
328    push_override_value_list(
329        &format!("optdepends_{architecture}"),
330        &properties.optional_dependencies,
331        output,
332    );
333}