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}