1use std::{collections::HashMap, str::FromStr};
4
5use alpm_parsers::iter_str_context;
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 Url,
23};
24use strum::VariantNames;
25use winnow::{
26 ModalResult,
27 Parser,
28 combinator::{alt, cut_err},
29 error::{ContextError, ErrMode, ParseError, StrContext},
30 token::rest,
31};
32
33use super::ensure_no_suffix;
34use crate::{
35 pkgbuild_bridge::error::BridgeError,
36 source_info::{
37 parser::{RelationKeyword, SharedMetaKeyword},
38 v1::package::{Override, Package, PackageArchitecture},
39 },
40};
41
42pub(crate) fn handle_packages(
64 base_package: Name,
65 valid_packages: Vec<Name>,
66 raw_values: HashMap<RawPackageName, HashMap<Keyword, ClearableValue>>,
67) -> Result<Vec<Package>, BridgeError> {
68 let mut package_map: HashMap<Name, Package> = HashMap::new();
69
70 for (name, values) in raw_values {
71 let name = if let Some(name) = name.0 {
74 Name::parser
75 .parse(&name)
76 .map_err(|err| BridgeError::InvalidPackageName {
77 name: name.clone(),
78 error: err.into(),
79 })?
80 } else {
81 if valid_packages.len() > 1 {
84 return Err(BridgeError::UnusedPackageFunction(base_package));
85 }
86
87 base_package.clone()
88 };
89
90 if !valid_packages.contains(&name) {
92 return Err(BridgeError::UndeclaredPackageName(name.to_string()));
93 }
94
95 let package = package_map.entry(name.clone()).or_insert(name.into());
97
98 handle_package(package, values)?;
99 }
100
101 let mut packages = Vec::new();
103 for name in valid_packages {
104 let Some(package) = package_map.remove(&name) else {
105 packages.push(name.into());
108 continue;
109 };
110
111 packages.push(package);
112 }
113
114 Ok(packages)
115}
116
117enum PackageKeyword {
119 Relation(RelationKeyword),
120 SharedMeta(SharedMetaKeyword),
121}
122
123impl PackageKeyword {
124 pub fn parser(input: &mut &str) -> ModalResult<PackageKeyword> {
132 cut_err(alt((
133 RelationKeyword::parser.map(PackageKeyword::Relation),
134 SharedMetaKeyword::parser.map(PackageKeyword::SharedMeta),
135 )))
136 .context(StrContext::Label("package base property type"))
137 .context_with(iter_str_context!([
138 RelationKeyword::VARIANTS,
139 SharedMetaKeyword::VARIANTS,
140 ]))
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<&'a str, O, ErrMode<ContextError>>>(
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.parse(value).map_err(|err| (keyword.clone(), err))?;
187
188 Ok(Override::Yes {
189 value: parsed_value,
190 })
191}
192
193fn parse_clearable_value_array<'a, O, P: Parser<&'a str, O, ErrMode<ContextError>>>(
211 keyword: &Keyword,
212 value: &'a ClearableValue,
213 mut parser: P,
214) -> Result<Override<Vec<O>>, BridgeError> {
215 let values = match value {
216 ClearableValue::Single(value) => {
217 let Some(value) = value else {
218 return Ok(Override::Clear);
219 };
220 if value.is_empty() {
222 return Ok(Override::Clear);
223 }
224 let value = parser.parse(value).map_err(|err| (keyword.clone(), err))?;
225
226 vec![value]
227 }
228 ClearableValue::Array(values) => {
229 let Some(values) = values else {
230 return Ok(Override::Clear);
231 };
232
233 values
234 .iter()
235 .map(|item| parser.parse(item).map_err(|err| (keyword.clone(), err)))
236 .collect::<Result<Vec<O>, (Keyword, ParseError<&'a str, ContextError>)>>()?
237 }
238 };
239
240 Ok(Override::Yes { value: values })
241}
242
243macro_rules! package_value_array {
248 (
249 $keyword:expr,
250 $value:expr,
251 $package:ident,
252 $field_name:ident,
253 $architecture:ident,
254 $parser:expr,
255 ) => {
256 if let Some(architecture) = $architecture {
257 let architecture_properties = $package
259 .architecture_properties
260 .entry(architecture)
261 .or_insert(PackageArchitecture::default());
262
263 architecture_properties.$field_name =
265 parse_clearable_value_array($keyword, $value, $parser)?;
266 } else {
267 $package.$field_name = parse_clearable_value_array($keyword, $value, $parser)?;
268 }
269 };
270}
271
272fn handle_package(
284 package: &mut Package,
285 values: HashMap<Keyword, ClearableValue>,
286) -> Result<(), BridgeError> {
287 for (raw_keyword, value) in values {
288 let keyword = PackageKeyword::parser
290 .parse(&raw_keyword.keyword)
291 .map_err(|err| (raw_keyword.clone(), err))?;
292
293 let architecture = match &raw_keyword.suffix {
295 Some(suffix) => {
296 let arch = Architecture::parser
297 .parse(suffix)
298 .map_err(|err| (raw_keyword.clone(), err))?;
299 Some(arch)
300 }
301 None => None,
302 };
303
304 match keyword {
306 PackageKeyword::Relation(keyword) => match keyword {
307 RelationKeyword::Depends => package_value_array!(
308 &raw_keyword,
309 &value,
310 package,
311 dependencies,
312 architecture,
313 RelationOrSoname::parser,
314 ),
315 RelationKeyword::OptDepends => package_value_array!(
316 &raw_keyword,
317 &value,
318 package,
319 optional_dependencies,
320 architecture,
321 OptionalDependency::parser,
322 ),
323 RelationKeyword::Provides => package_value_array!(
324 &raw_keyword,
325 &value,
326 package,
327 provides,
328 architecture,
329 RelationOrSoname::parser,
330 ),
331 RelationKeyword::Conflicts => package_value_array!(
332 &raw_keyword,
333 &value,
334 package,
335 conflicts,
336 architecture,
337 PackageRelation::parser,
338 ),
339 RelationKeyword::Replaces => package_value_array!(
340 &raw_keyword,
341 &value,
342 package,
343 replaces,
344 architecture,
345 PackageRelation::parser,
346 ),
347 },
348 PackageKeyword::SharedMeta(keyword) => match keyword {
349 SharedMetaKeyword::PkgDesc => {
350 ensure_no_suffix(&raw_keyword, architecture)?;
351 package.description = parse_clearable_value(
352 &raw_keyword,
353 &value,
354 rest.try_map(PackageDescription::from_str),
355 )?;
356 }
357 SharedMetaKeyword::Url => {
358 ensure_no_suffix(&raw_keyword, architecture)?;
359 package.url =
360 parse_clearable_value(&raw_keyword, &value, rest.try_map(Url::from_str))?;
361 }
362 SharedMetaKeyword::License => {
363 ensure_no_suffix(&raw_keyword, architecture)?;
364 package.licenses = parse_clearable_value_array(
365 &raw_keyword,
366 &value,
367 rest.try_map(License::from_str),
368 )?;
369 }
370 SharedMetaKeyword::Arch => {
371 ensure_no_suffix(&raw_keyword, architecture)?;
372 let archs = parse_clearable_value_array(
373 &raw_keyword,
374 &value,
375 rest.try_map(Architecture::from_str),
376 )?;
377
378 package.architectures = match archs {
380 Override::No => None,
381 Override::Clear => {
382 return Err(BridgeError::UnclearableValue {
383 keyword: raw_keyword,
384 });
385 }
386 Override::Yes { value } => {
387 let mut architectures = Vec::new();
388 for arch in value {
390 if architectures.contains(&arch) {
391 return Err(BridgeError::DuplicateArchitecture {
392 duplicate: arch,
393 });
394 }
395 architectures.push(arch);
396 }
397 Some(architectures)
398 }
399 };
400 }
401 SharedMetaKeyword::Changelog => {
402 ensure_no_suffix(&raw_keyword, architecture)?;
403 package.changelog = parse_clearable_value(
404 &raw_keyword,
405 &value,
406 rest.try_map(Changelog::from_str),
407 )?;
408 }
409 SharedMetaKeyword::Install => {
410 ensure_no_suffix(&raw_keyword, architecture)?;
411 package.install = parse_clearable_value(
412 &raw_keyword,
413 &value,
414 rest.try_map(Install::from_str),
415 )?;
416 }
417 SharedMetaKeyword::Groups => {
418 ensure_no_suffix(&raw_keyword, architecture)?;
419 package.groups = parse_clearable_value_array(
420 &raw_keyword,
421 &value,
422 rest.try_map(Group::from_str),
423 )?;
424 }
425 SharedMetaKeyword::Options => {
426 ensure_no_suffix(&raw_keyword, architecture)?;
427 package.options = parse_clearable_value_array(
428 &raw_keyword,
429 &value,
430 rest.try_map(MakepkgOption::from_str),
431 )?;
432 }
433 SharedMetaKeyword::Backup => {
434 ensure_no_suffix(&raw_keyword, architecture)?;
435 package.backups = parse_clearable_value_array(
436 &raw_keyword,
437 &value,
438 rest.try_map(Backup::from_str),
439 )?;
440 }
441 },
442 }
443 }
444
445 Ok(())
446}