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