1use std::{collections::HashMap, str::FromStr};
4
5use alpm_parsers::{
6 iter_str_context,
7 traits::{AlpmParser, ParserUntil},
8};
9#[cfg(doc)]
10use alpm_pkgbuild::bridge::BridgeOutput;
11use alpm_pkgbuild::bridge::{ClearableValue, Keyword, RawPackageName};
12use alpm_types::{
13 Architecture,
14 Backup,
15 Changelog,
16 Group,
17 Install,
18 License,
19 MakepkgOption,
20 Name,
21 OptionalDependency,
22 PackageDescription,
23 PackageRelation,
24 RelationOrSoname,
25 SystemArchitecture,
26 Url,
27};
28use strum::VariantNames;
29use winnow::{
30 ModalResult,
31 Parser,
32 combinator::{alt, cut_err},
33 error::{ContextError, ErrMode, ParseError, StrContext},
34 token::rest,
35};
36
37use super::ensure_no_suffix;
38use crate::{
39 pkgbuild_bridge::error::BridgeError,
40 source_info::{
41 parser::{RelationKeyword, SharedMetaKeyword},
42 v1::package::{Override, Package, PackageArchitecture},
43 },
44};
45
46pub(crate) fn handle_packages(
68 base_package: Name,
69 valid_packages: Vec<Name>,
70 raw_values: HashMap<RawPackageName, HashMap<Keyword, ClearableValue>>,
71) -> Result<Vec<Package>, BridgeError> {
72 let mut package_map: HashMap<Name, Package> = HashMap::new();
73
74 for (name, values) in raw_values {
75 let name = if let Some(name) = name.0 {
78 Name::parser
79 .parse(&name)
80 .map_err(|err| BridgeError::InvalidPackageName {
81 name: name.clone(),
82 error: err.into(),
83 })?
84 } else {
85 if valid_packages.len() > 1 {
88 return Err(BridgeError::UnusedPackageFunction(base_package));
89 }
90
91 base_package.clone()
92 };
93
94 if !valid_packages.contains(&name) {
96 return Err(BridgeError::UndeclaredPackageName(name.to_string()));
97 }
98
99 let package = package_map.entry(name.clone()).or_insert(name.into());
101
102 handle_package(package, values)?;
103 }
104
105 let mut packages = Vec::new();
107 for name in valid_packages {
108 let Some(package) = package_map.remove(&name) else {
109 packages.push(name.into());
112 continue;
113 };
114
115 packages.push(package);
116 }
117
118 Ok(packages)
119}
120
121enum PackageKeyword {
123 Relation(RelationKeyword),
124 SharedMeta(SharedMetaKeyword),
125}
126
127impl PackageKeyword {
128 pub fn parser(input: &mut &str) -> ModalResult<PackageKeyword> {
136 cut_err(alt((
137 RelationKeyword::parser.map(PackageKeyword::Relation),
138 SharedMetaKeyword::parser.map(PackageKeyword::SharedMeta),
139 )))
140 .context(StrContext::Label("package base property type"))
141 .context_with(iter_str_context!([
142 RelationKeyword::VARIANTS,
143 SharedMetaKeyword::VARIANTS,
144 ]))
145 .parse_next(input)
146 }
147}
148
149fn ensure_single_clearable_value<'a>(
156 keyword: &Keyword,
157 value: &'a ClearableValue,
158) -> Result<&'a Option<String>, BridgeError> {
159 match value {
160 ClearableValue::Single(value) => Ok(value),
161 ClearableValue::Array(values) => Err(BridgeError::UnexpectedArray {
162 keyword: keyword.clone(),
163 values: values.clone().unwrap_or_default().clone(),
164 }),
165 }
166}
167
168fn parse_clearable_value<'a, O, P: Parser<&'a str, O, ErrMode<ContextError>>>(
178 keyword: &Keyword,
179 value: &'a ClearableValue,
180 mut parser: P,
181) -> Result<Override<O>, BridgeError> {
182 let value = ensure_single_clearable_value(keyword, value)?;
184
185 let Some(value) = value else {
187 return Ok(Override::Clear);
188 };
189
190 let parsed_value = parser.parse(value).map_err(|err| (keyword.clone(), err))?;
191
192 Ok(Override::Yes {
193 value: parsed_value,
194 })
195}
196
197fn parse_clearable_value_array<'a, O, P: Parser<&'a str, O, ErrMode<ContextError>>>(
215 keyword: &Keyword,
216 value: &'a ClearableValue,
217 mut parser: P,
218) -> Result<Override<Vec<O>>, BridgeError> {
219 let values = match value {
220 ClearableValue::Single(value) => {
221 let Some(value) = value else {
222 return Ok(Override::Clear);
223 };
224 if value.is_empty() {
226 return Ok(Override::Clear);
227 }
228 let value = parser.parse(value).map_err(|err| (keyword.clone(), err))?;
229
230 vec![value]
231 }
232 ClearableValue::Array(values) => {
233 let Some(values) = values else {
234 return Ok(Override::Clear);
235 };
236
237 values
238 .iter()
239 .map(|item| parser.parse(item).map_err(|err| (keyword.clone(), err)))
240 .collect::<Result<Vec<O>, (Keyword, ParseError<&'a str, ContextError>)>>()?
241 }
242 };
243
244 Ok(Override::Yes { value: values })
245}
246
247macro_rules! package_value_array {
252 (
253 $keyword:expr,
254 $value:expr,
255 $package:ident,
256 $field_name:ident,
257 $architecture:ident,
258 $parser:expr,
259 ) => {
260 if let Some(architecture) = $architecture {
261 let architecture_properties = $package
263 .architecture_properties
264 .entry(architecture)
265 .or_insert(PackageArchitecture::default());
266
267 architecture_properties.$field_name =
269 parse_clearable_value_array($keyword, $value, $parser)?;
270 } else {
271 $package.$field_name = parse_clearable_value_array($keyword, $value, $parser)?;
272 }
273 };
274}
275
276fn handle_package(
288 package: &mut Package,
289 values: HashMap<Keyword, ClearableValue>,
290) -> Result<(), BridgeError> {
291 for (raw_keyword, value) in values {
292 let keyword = PackageKeyword::parser
294 .parse(&raw_keyword.keyword)
295 .map_err(|err| (raw_keyword.clone(), err))?;
296
297 let architecture = match &raw_keyword.suffix {
299 Some(suffix) => {
300 let arch = SystemArchitecture::parser
302 .parse(suffix)
303 .map_err(|err| (raw_keyword.clone(), err))?;
304 Some(arch)
305 }
306 None => None,
307 };
308
309 match keyword {
311 PackageKeyword::Relation(keyword) => match keyword {
312 RelationKeyword::Depends => package_value_array!(
313 &raw_keyword,
314 &value,
315 package,
316 dependencies,
317 architecture,
318 RelationOrSoname::parser_until_eof,
319 ),
320 RelationKeyword::OptDepends => package_value_array!(
321 &raw_keyword,
322 &value,
323 package,
324 optional_dependencies,
325 architecture,
326 OptionalDependency::parser_until_eof,
327 ),
328 RelationKeyword::Provides => package_value_array!(
329 &raw_keyword,
330 &value,
331 package,
332 provides,
333 architecture,
334 RelationOrSoname::parser_until_eof,
335 ),
336 RelationKeyword::Conflicts => package_value_array!(
337 &raw_keyword,
338 &value,
339 package,
340 conflicts,
341 architecture,
342 PackageRelation::parser_until_eof,
343 ),
344 RelationKeyword::Replaces => package_value_array!(
345 &raw_keyword,
346 &value,
347 package,
348 replaces,
349 architecture,
350 PackageRelation::parser_until_eof,
351 ),
352 },
353 PackageKeyword::SharedMeta(keyword) => match keyword {
354 SharedMetaKeyword::PkgDesc => {
355 ensure_no_suffix(&raw_keyword, architecture)?;
356 package.description = parse_clearable_value(
357 &raw_keyword,
358 &value,
359 rest.try_map(PackageDescription::from_str),
360 )?;
361 }
362 SharedMetaKeyword::Url => {
363 ensure_no_suffix(&raw_keyword, architecture)?;
364 package.url =
365 parse_clearable_value(&raw_keyword, &value, rest.try_map(Url::from_str))?;
366 }
367 SharedMetaKeyword::License => {
368 ensure_no_suffix(&raw_keyword, architecture)?;
369 package.licenses = parse_clearable_value_array(
370 &raw_keyword,
371 &value,
372 rest.try_map(License::from_str),
373 )?;
374 }
375 SharedMetaKeyword::Arch => {
376 ensure_no_suffix(&raw_keyword, architecture)?;
377 let archs = parse_clearable_value_array(
378 &raw_keyword,
379 &value,
380 Architecture::parser_until_eof,
381 )?;
382
383 package.architectures = match archs {
385 Override::No => None,
386 Override::Clear => {
387 return Err(BridgeError::UnclearableValue {
388 keyword: raw_keyword,
389 });
390 }
391 Override::Yes { value } => Some(value.try_into()?),
392 };
393 }
394 SharedMetaKeyword::Changelog => {
395 ensure_no_suffix(&raw_keyword, architecture)?;
396 package.changelog = parse_clearable_value(
397 &raw_keyword,
398 &value,
399 rest.try_map(Changelog::from_str),
400 )?;
401 }
402 SharedMetaKeyword::Install => {
403 ensure_no_suffix(&raw_keyword, architecture)?;
404 package.install = parse_clearable_value(
405 &raw_keyword,
406 &value,
407 rest.try_map(Install::from_str),
408 )?;
409 }
410 SharedMetaKeyword::Groups => {
411 ensure_no_suffix(&raw_keyword, architecture)?;
412 package.groups = parse_clearable_value_array(
413 &raw_keyword,
414 &value,
415 rest.try_map(Group::from_str),
416 )?;
417 }
418 SharedMetaKeyword::Options => {
419 ensure_no_suffix(&raw_keyword, architecture)?;
420 package.options = parse_clearable_value_array(
421 &raw_keyword,
422 &value,
423 MakepkgOption::parser_until_eof,
424 )?;
425 }
426 SharedMetaKeyword::Backup => {
427 ensure_no_suffix(&raw_keyword, architecture)?;
428 package.backups = parse_clearable_value_array(
429 &raw_keyword,
430 &value,
431 rest.try_map(Backup::from_str),
432 )?;
433 }
434 },
435 }
436 }
437
438 Ok(())
439}