1use std::{collections::HashMap, str::FromStr};
4
5use alpm_parsers::prelude::*;
6#[cfg(doc)]
7use alpm_pkgbuild::bridge::BridgeOutput;
8use alpm_pkgbuild::bridge::{ClearableValue, Keyword, RawPackageName};
9use alpm_types::{
10 Architecture,
11 Backup,
12 Changelog,
13 Group,
14 Install,
15 License,
16 MakepkgOption,
17 Name,
18 OptionalDependency,
19 PackageDescription,
20 PackageRelation,
21 RelationOrSoname,
22 SystemArchitecture,
23 Url,
24};
25use strum::VariantNames;
26use winnow::{
27 combinator::{alt, cut_err},
28 error::{ErrMode, ParseError},
29 token::rest,
30};
31
32use super::ensure_no_suffix;
33use crate::{
34 pkgbuild_bridge::error::BridgeError,
35 source_info::{
36 parser::{RelationKeyword, SharedMetaKeyword},
37 v1::package::{Override, Package, PackageArchitecture},
38 },
39};
40
41pub(crate) fn handle_packages(
63 base_package: Name,
64 valid_packages: Vec<Name>,
65 raw_values: HashMap<RawPackageName, HashMap<Keyword, ClearableValue>>,
66) -> Result<Vec<Package>, BridgeError> {
67 let mut package_map: HashMap<Name, Package> = HashMap::new();
68
69 for (name, values) in raw_values {
70 let name = if let Some(name) = name.0 {
73 Name::parser.parse(Input::new(&name)).map_err(|err| {
74 BridgeError::InvalidPackageName {
75 name: name.clone(),
76 error: err.into(),
77 }
78 })?
79 } else {
80 if valid_packages.len() > 1 {
83 return Err(BridgeError::UnusedPackageFunction(base_package));
84 }
85
86 base_package.clone()
87 };
88
89 if !valid_packages.contains(&name) {
91 return Err(BridgeError::UndeclaredPackageName(name.to_string()));
92 }
93
94 let package = package_map.entry(name.clone()).or_insert(name.into());
96
97 handle_package(package, values)?;
98 }
99
100 let mut packages = Vec::new();
102 for name in valid_packages {
103 let Some(package) = package_map.remove(&name) else {
104 packages.push(name.into());
107 continue;
108 };
109
110 packages.push(package);
111 }
112
113 Ok(packages)
114}
115
116enum PackageKeyword {
118 Relation(RelationKeyword),
119 SharedMeta(SharedMetaKeyword),
120}
121
122impl PackageKeyword {
123 pub fn parser<'a>(input: &mut Input<'a>) -> PResult<'a, PackageKeyword> {
131 cut_err(alt((
132 RelationKeyword::parser.map(PackageKeyword::Relation),
133 SharedMetaKeyword::parser.map(PackageKeyword::SharedMeta),
134 )))
135 .expected_strings(
136 [RelationKeyword::VARIANTS, SharedMetaKeyword::VARIANTS]
137 .into_iter()
138 .flatten(),
139 )
140 .layer("package base property keyword")
141 .parse_next(input)
142 }
143}
144
145fn ensure_single_clearable_value<'a>(
152 keyword: &Keyword,
153 value: &'a ClearableValue,
154) -> Result<&'a Option<String>, BridgeError> {
155 match value {
156 ClearableValue::Single(value) => Ok(value),
157 ClearableValue::Array(values) => Err(BridgeError::UnexpectedArray {
158 keyword: keyword.clone(),
159 values: values.clone().unwrap_or_default().clone(),
160 }),
161 }
162}
163
164fn parse_clearable_value<'a, O, P: Parser<Input<'a>, O, ErrMode<ParseStack<'a>>>>(
174 keyword: &Keyword,
175 value: &'a ClearableValue,
176 mut parser: P,
177) -> Result<Override<O>, BridgeError> {
178 let value = ensure_single_clearable_value(keyword, value)?;
180
181 let Some(value) = value else {
183 return Ok(Override::Clear);
184 };
185
186 let parsed_value = parser
187 .parse(Input::new(value))
188 .map_err(|err| (keyword.clone(), err))?;
189
190 Ok(Override::Yes {
191 value: parsed_value,
192 })
193}
194
195#[expect(clippy::result_large_err)]
214fn parse_clearable_value_array<'a, O, P: Parser<Input<'a>, O, ErrMode<ParseStack<'a>>>>(
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
229 .parse(Input::new(value))
230 .map_err(|err| (keyword.clone(), err))?;
231
232 vec![value]
233 }
234 ClearableValue::Array(values) => {
235 let Some(values) = values else {
236 return Ok(Override::Clear);
237 };
238
239 values
240 .iter()
241 .map(|item| {
242 parser
243 .parse(Input::new(item))
244 .map_err(|err| (keyword.clone(), err))
245 })
246 .collect::<Result<Vec<O>, (Keyword, ParseError<Input<'a>, ParseStack<'a>>)>>()?
247 }
248 };
249
250 Ok(Override::Yes { value: values })
251}
252
253macro_rules! package_value_array {
258 (
259 $keyword:expr,
260 $value:expr,
261 $package:ident,
262 $field_name:ident,
263 $architecture:ident,
264 $parser:expr,
265 ) => {
266 if let Some(architecture) = $architecture {
267 let architecture_properties = $package
269 .architecture_properties
270 .entry(architecture)
271 .or_insert(PackageArchitecture::default());
272
273 architecture_properties.$field_name =
275 parse_clearable_value_array($keyword, $value, $parser)?;
276 } else {
277 $package.$field_name = parse_clearable_value_array($keyword, $value, $parser)?;
278 }
279 };
280}
281
282fn handle_package(
294 package: &mut Package,
295 values: HashMap<Keyword, ClearableValue>,
296) -> Result<(), BridgeError> {
297 for (raw_keyword, value) in values {
298 let keyword = PackageKeyword::parser
300 .parse(Input::new(&raw_keyword.keyword))
301 .map_err(|err| (raw_keyword.clone(), err))?;
302
303 let architecture = match &raw_keyword.suffix {
305 Some(suffix) => {
306 let arch = SystemArchitecture::parser
308 .parse(Input::new(suffix))
309 .map_err(|err| (raw_keyword.clone(), err))?;
310 Some(arch)
311 }
312 None => None,
313 };
314
315 match keyword {
317 PackageKeyword::Relation(keyword) => match keyword {
318 RelationKeyword::Depends => package_value_array!(
319 &raw_keyword,
320 &value,
321 package,
322 dependencies,
323 architecture,
324 RelationOrSoname::parser_until_eof,
325 ),
326 RelationKeyword::OptDepends => package_value_array!(
327 &raw_keyword,
328 &value,
329 package,
330 optional_dependencies,
331 architecture,
332 OptionalDependency::parser_until_eof,
333 ),
334 RelationKeyword::Provides => package_value_array!(
335 &raw_keyword,
336 &value,
337 package,
338 provides,
339 architecture,
340 RelationOrSoname::parser_until_eof,
341 ),
342 RelationKeyword::Conflicts => package_value_array!(
343 &raw_keyword,
344 &value,
345 package,
346 conflicts,
347 architecture,
348 PackageRelation::parser_until_eof,
349 ),
350 RelationKeyword::Replaces => package_value_array!(
351 &raw_keyword,
352 &value,
353 package,
354 replaces,
355 architecture,
356 PackageRelation::parser_until_eof,
357 ),
358 },
359 PackageKeyword::SharedMeta(keyword) => match keyword {
360 SharedMetaKeyword::PkgDesc => {
361 ensure_no_suffix(&raw_keyword, architecture)?;
362 package.description = parse_clearable_value(
363 &raw_keyword,
364 &value,
365 rest.try_map(PackageDescription::from_str),
366 )?;
367 }
368 SharedMetaKeyword::Url => {
369 ensure_no_suffix(&raw_keyword, architecture)?;
370 package.url =
371 parse_clearable_value(&raw_keyword, &value, rest.try_map(Url::from_str))?;
372 }
373 SharedMetaKeyword::License => {
374 ensure_no_suffix(&raw_keyword, architecture)?;
375 package.licenses = parse_clearable_value_array(
376 &raw_keyword,
377 &value,
378 rest.try_map(License::from_str),
379 )?;
380 }
381 SharedMetaKeyword::Arch => {
382 ensure_no_suffix(&raw_keyword, architecture)?;
383 let archs = parse_clearable_value_array(
384 &raw_keyword,
385 &value,
386 Architecture::parser_until_eof,
387 )?;
388
389 package.architectures = match archs {
391 Override::No => None,
392 Override::Clear => {
393 return Err(BridgeError::UnclearableValue {
394 keyword: raw_keyword,
395 });
396 }
397 Override::Yes { value } => Some(value.try_into()?),
398 };
399 }
400 SharedMetaKeyword::Changelog => {
401 ensure_no_suffix(&raw_keyword, architecture)?;
402 package.changelog = parse_clearable_value(
403 &raw_keyword,
404 &value,
405 rest.try_map(Changelog::from_str),
406 )?;
407 }
408 SharedMetaKeyword::Install => {
409 ensure_no_suffix(&raw_keyword, architecture)?;
410 package.install = parse_clearable_value(
411 &raw_keyword,
412 &value,
413 rest.try_map(Install::from_str),
414 )?;
415 }
416 SharedMetaKeyword::Groups => {
417 ensure_no_suffix(&raw_keyword, architecture)?;
418 package.groups = parse_clearable_value_array(
419 &raw_keyword,
420 &value,
421 rest.try_map(Group::from_str),
422 )?;
423 }
424 SharedMetaKeyword::Options => {
425 ensure_no_suffix(&raw_keyword, architecture)?;
426 package.options = parse_clearable_value_array(
427 &raw_keyword,
428 &value,
429 MakepkgOption::parser_until_eof,
430 )?;
431 }
432 SharedMetaKeyword::Backup => {
433 ensure_no_suffix(&raw_keyword, architecture)?;
434 package.backups = parse_clearable_value_array(
435 &raw_keyword,
436 &value,
437 rest.try_map(Backup::from_str),
438 )?;
439 }
440 },
441 }
442 }
443
444 Ok(())
445}