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