1use std::{
4 collections::{BTreeMap, HashMap},
5 str::FromStr,
6};
7
8use alpm_parsers::iter_str_context;
9#[cfg(doc)]
10use alpm_pkgbuild::bridge::BridgeOutput;
11use alpm_pkgbuild::bridge::{Keyword, Value};
12use alpm_types::{
13 Architectures,
14 Backup,
15 Changelog,
16 Epoch,
17 FullVersion,
18 Install,
19 License,
20 MakepkgOption,
21 Name,
22 OpenPGPIdentifier,
23 OptionalDependency,
24 PackageDescription,
25 PackageRelation,
26 PackageRelease,
27 PackageVersion,
28 RelationOrSoname,
29 SkippableChecksum,
30 Source,
31 SystemArchitecture,
32 Url,
33};
34use strum::VariantNames;
35use winnow::{
36 ModalResult,
37 Parser,
38 combinator::{alt, cut_err},
39 error::StrContext,
40 token::rest,
41};
42
43use crate::{
44 pkgbuild_bridge::{
45 ensure_keyword_exists,
46 ensure_no_suffix,
47 error::BridgeError,
48 parse_arch_array,
49 parse_optional_value,
50 parse_value,
51 parse_value_array,
52 },
53 source_info::{
54 parser::{PackageBaseKeyword, RelationKeyword, SharedMetaKeyword, SourceKeyword},
55 v1::package_base::{PackageBase, PackageBaseArchitecture},
56 },
57};
58
59enum PackageBaseKeywords {
61 PkgBase,
63 PackageBase(PackageBaseKeyword),
64 Relation(RelationKeyword),
65 SharedMeta(SharedMetaKeyword),
66 Source(SourceKeyword),
67}
68
69impl PackageBaseKeywords {
70 pub fn parser(input: &mut &str) -> ModalResult<PackageBaseKeywords> {
78 cut_err(alt((
79 "pkgbase".map(|_| PackageBaseKeywords::PkgBase),
80 PackageBaseKeyword::parser.map(PackageBaseKeywords::PackageBase),
81 RelationKeyword::parser.map(PackageBaseKeywords::Relation),
82 SharedMetaKeyword::parser.map(PackageBaseKeywords::SharedMeta),
83 SourceKeyword::parser.map(PackageBaseKeywords::Source),
84 )))
85 .context(StrContext::Label("package base property type"))
86 .context_with(iter_str_context!([
87 &["pkgbase"],
88 PackageBaseKeyword::VARIANTS,
89 RelationKeyword::VARIANTS,
90 SharedMetaKeyword::VARIANTS,
91 SourceKeyword::VARIANTS,
92 ]))
93 .parse_next(input)
94 }
95}
96
97macro_rules! package_base_value_array {
103 (
104 $keyword:ident,
105 $value:ident,
106 $field_name:ident,
107 $architecture:ident,
108 $architecture_properties:ident,
109 $parser:expr,
110 ) => {
111 if let Some(architecture) = $architecture {
112 let architecture_properties = $architecture_properties
114 .entry(architecture)
115 .or_insert(PackageBaseArchitecture::default());
116
117 architecture_properties.$field_name = parse_value_array($keyword, $value, $parser)?;
119 } else {
120 $field_name = parse_value_array($keyword, $value, $parser)?;
121 }
122 };
123}
124
125pub fn handle_package_base(
137 name: Name,
138 mut raw: HashMap<Keyword, Value>,
139) -> Result<PackageBase, BridgeError> {
140 let pkgver_keyword = Keyword::simple("pkgver");
142 let value = ensure_keyword_exists(&pkgver_keyword, &mut raw)?;
143 let package_version: PackageVersion =
144 parse_value(&pkgver_keyword, &value, PackageVersion::parser)?;
145
146 let pkgrel_keyword = Keyword::simple("pkgrel");
147 let value = ensure_keyword_exists(&pkgrel_keyword, &mut raw)?;
148 let package_release: PackageRelease =
149 parse_value(&pkgver_keyword, &value, PackageRelease::parser)?;
150
151 let mut description = None;
152 let mut url = None;
153 let mut licenses = Vec::new();
154 let mut changelog = None;
155 let mut architectures = Architectures::Some(Vec::new());
156 let mut architecture_properties = BTreeMap::new();
157
158 let mut install = None;
160 let mut groups = Vec::new();
161 let mut options = Vec::new();
162 let mut backups = Vec::new();
163
164 let mut epoch: Option<Epoch> = None;
165 let mut pgp_fingerprints = Vec::new();
166
167 let mut dependencies = Vec::new();
168 let mut optional_dependencies = Vec::new();
169 let mut provides = Vec::new();
170 let mut conflicts = Vec::new();
171 let mut replaces = Vec::new();
172 let mut check_dependencies = Vec::new();
176 let mut make_dependencies = Vec::new();
177
178 let mut sources = Vec::new();
179 let mut no_extracts = Vec::new();
180 let mut b2_checksums = Vec::new();
181 let mut md5_checksums = Vec::new();
182 let mut sha1_checksums = Vec::new();
183 let mut sha224_checksums = Vec::new();
184 let mut sha256_checksums = Vec::new();
185 let mut sha384_checksums = Vec::new();
186 let mut sha512_checksums = Vec::new();
187 let mut crc_checksums = Vec::new();
188
189 for (raw_keyword, value) in &raw {
191 let keyword = PackageBaseKeywords::parser
193 .parse(&raw_keyword.keyword)
194 .map_err(|err| (raw_keyword.clone(), err))?;
195
196 let architecture = match &raw_keyword.suffix {
198 Some(suffix) => {
199 let arch = SystemArchitecture::parser
201 .parse(suffix)
202 .map_err(|err| (raw_keyword.clone(), err))?;
203 Some(arch)
204 }
205 None => None,
206 };
207
208 match keyword {
210 PackageBaseKeywords::PkgBase => {
211 ensure_no_suffix(raw_keyword, architecture)?;
214 unreachable!(
215 "'pkgbase' has been handled before and should no longer exist without a suffix."
216 )
217 }
218 PackageBaseKeywords::PackageBase(keyword) => match keyword {
219 PackageBaseKeyword::PkgVer => {
222 ensure_no_suffix(raw_keyword, architecture)?;
223 }
224 PackageBaseKeyword::PkgRel => {
225 ensure_no_suffix(raw_keyword, architecture)?;
226 }
227 PackageBaseKeyword::Epoch => {
228 ensure_no_suffix(raw_keyword, architecture)?;
229 epoch = parse_optional_value(raw_keyword, value, Epoch::parser)?;
230 }
231 PackageBaseKeyword::ValidPGPKeys => {
232 ensure_no_suffix(raw_keyword, architecture)?;
233 pgp_fingerprints = parse_value_array(
234 raw_keyword,
235 value,
236 rest.try_map(OpenPGPIdentifier::from_str),
237 )?;
238 }
239 PackageBaseKeyword::CheckDepends => {
240 package_base_value_array!(
241 raw_keyword,
242 value,
243 check_dependencies,
244 architecture,
245 architecture_properties,
246 PackageRelation::parser,
247 )
248 }
249 PackageBaseKeyword::MakeDepends => package_base_value_array!(
250 raw_keyword,
251 value,
252 make_dependencies,
253 architecture,
254 architecture_properties,
255 PackageRelation::parser,
256 ),
257 },
258 PackageBaseKeywords::SharedMeta(keyword) => match keyword {
259 SharedMetaKeyword::PkgDesc => {
260 ensure_no_suffix(raw_keyword, architecture)?;
261 description = parse_optional_value(
262 raw_keyword,
263 value,
264 rest.try_map(PackageDescription::from_str),
265 )?;
266 }
267 SharedMetaKeyword::Url => {
268 ensure_no_suffix(raw_keyword, architecture)?;
269 url = parse_optional_value(raw_keyword, value, rest.try_map(Url::from_str))?;
270 }
271 SharedMetaKeyword::License => {
272 ensure_no_suffix(raw_keyword, architecture)?;
273 licenses =
274 parse_value_array(raw_keyword, value, rest.try_map(License::from_str))?;
275 }
276 SharedMetaKeyword::Arch => {
277 ensure_no_suffix(raw_keyword, architecture)?;
278 architectures = parse_arch_array(raw_keyword, value)?;
279 }
280 SharedMetaKeyword::Changelog => {
281 ensure_no_suffix(raw_keyword, architecture)?;
282 changelog = parse_optional_value(
283 raw_keyword,
284 value,
285 rest.try_map(Changelog::from_str),
286 )?;
287 }
288 SharedMetaKeyword::Install => {
289 ensure_no_suffix(raw_keyword, architecture)?;
290 install =
291 parse_optional_value(raw_keyword, value, rest.try_map(Install::from_str))?;
292 }
293 SharedMetaKeyword::Groups => {
294 ensure_no_suffix(raw_keyword, architecture)?;
295 groups = value.clone().as_owned_vec();
296 }
297 SharedMetaKeyword::Options => {
298 ensure_no_suffix(raw_keyword, architecture)?;
299 options = parse_value_array(raw_keyword, value, MakepkgOption::parser)?;
300 }
301 SharedMetaKeyword::Backup => {
302 ensure_no_suffix(raw_keyword, architecture)?;
303 backups =
304 parse_value_array(raw_keyword, value, rest.try_map(Backup::from_str))?;
305 }
306 },
307 PackageBaseKeywords::Relation(keyword) => match keyword {
308 RelationKeyword::Depends => package_base_value_array!(
309 raw_keyword,
310 value,
311 dependencies,
312 architecture,
313 architecture_properties,
314 RelationOrSoname::parser,
315 ),
316 RelationKeyword::OptDepends => package_base_value_array!(
317 raw_keyword,
318 value,
319 optional_dependencies,
320 architecture,
321 architecture_properties,
322 OptionalDependency::parser,
323 ),
324 RelationKeyword::Provides => package_base_value_array!(
325 raw_keyword,
326 value,
327 provides,
328 architecture,
329 architecture_properties,
330 RelationOrSoname::parser,
331 ),
332 RelationKeyword::Conflicts => package_base_value_array!(
333 raw_keyword,
334 value,
335 conflicts,
336 architecture,
337 architecture_properties,
338 PackageRelation::parser,
339 ),
340 RelationKeyword::Replaces => package_base_value_array!(
341 raw_keyword,
342 value,
343 replaces,
344 architecture,
345 architecture_properties,
346 PackageRelation::parser,
347 ),
348 },
349
350 PackageBaseKeywords::Source(keyword) => match keyword {
351 SourceKeyword::NoExtract => {
352 ensure_no_suffix(raw_keyword, architecture)?;
353 no_extracts = value.clone().as_owned_vec();
354 }
355 SourceKeyword::Source => package_base_value_array!(
356 raw_keyword,
357 value,
358 sources,
359 architecture,
360 architecture_properties,
361 rest.try_map(Source::from_str),
362 ),
363 SourceKeyword::B2sums => package_base_value_array!(
364 raw_keyword,
365 value,
366 b2_checksums,
367 architecture,
368 architecture_properties,
369 SkippableChecksum::parser,
370 ),
371 SourceKeyword::Md5sums => package_base_value_array!(
372 raw_keyword,
373 value,
374 md5_checksums,
375 architecture,
376 architecture_properties,
377 SkippableChecksum::parser,
378 ),
379 SourceKeyword::Sha1sums => package_base_value_array!(
380 raw_keyword,
381 value,
382 sha1_checksums,
383 architecture,
384 architecture_properties,
385 SkippableChecksum::parser,
386 ),
387 SourceKeyword::Sha224sums => package_base_value_array!(
388 raw_keyword,
389 value,
390 sha224_checksums,
391 architecture,
392 architecture_properties,
393 SkippableChecksum::parser,
394 ),
395 SourceKeyword::Sha256sums => package_base_value_array!(
396 raw_keyword,
397 value,
398 sha256_checksums,
399 architecture,
400 architecture_properties,
401 SkippableChecksum::parser,
402 ),
403 SourceKeyword::Sha384sums => package_base_value_array!(
404 raw_keyword,
405 value,
406 sha384_checksums,
407 architecture,
408 architecture_properties,
409 SkippableChecksum::parser,
410 ),
411 SourceKeyword::Sha512sums => package_base_value_array!(
412 raw_keyword,
413 value,
414 sha512_checksums,
415 architecture,
416 architecture_properties,
417 SkippableChecksum::parser,
418 ),
419 SourceKeyword::Cksums => package_base_value_array!(
420 raw_keyword,
421 value,
422 crc_checksums,
423 architecture,
424 architecture_properties,
425 SkippableChecksum::parser,
426 ),
427 },
428 }
429 }
430
431 let version = FullVersion::new(package_version, package_release, epoch);
432
433 Ok(PackageBase {
434 name,
435 description,
436 url,
437 changelog,
438 licenses,
439 install,
440 groups,
441 options,
442 backups,
443 version,
444 pgp_fingerprints,
445 architectures,
446 architecture_properties,
447 dependencies,
448 optional_dependencies,
449 provides,
450 conflicts,
451 replaces,
452 check_dependencies,
453 make_dependencies,
454 sources,
455 no_extracts,
456 b2_checksums,
457 md5_checksums,
458 sha1_checksums,
459 sha224_checksums,
460 sha256_checksums,
461 sha384_checksums,
462 sha512_checksums,
463 crc_checksums,
464 })
465}