pub mod error;
mod package;
mod package_base;
use std::collections::HashMap;
use alpm_pkgbuild::bridge::{BridgeOutput, Keyword, Value};
use alpm_types::{Architectures, Name, SystemArchitecture};
use package::handle_packages;
use package_base::handle_package_base;
use winnow::{
Parser,
error::{ContextError, ErrMode, ParseError, StrContext, StrContextValue},
};
use crate::{SourceInfoV1, pkgbuild_bridge::error::BridgeError};
impl TryFrom<BridgeOutput> for SourceInfoV1 {
type Error = BridgeError;
fn try_from(mut value: BridgeOutput) -> Result<Self, Self::Error> {
let mut name = None;
let pkgbase_keyword = Keyword::simple("pkgbase");
if let Some(value) = value.package_base.remove(&pkgbase_keyword) {
name = Some(parse_value(&pkgbase_keyword, &value, Name::parser)?);
}
let pkgname_keyword = Keyword::simple("pkgname");
let names = ensure_keyword_exists(&pkgname_keyword, &mut value.package_base)?;
let names = parse_value_array(&pkgname_keyword, &names, Name::parser)?;
let name = match name {
Some(name) => name,
None => {
names.first().cloned().ok_or(BridgeError::NoName)?
}
};
let base = handle_package_base(name.clone(), value.package_base)?;
for name in value.functions {
let Some(name) = name.0 else { continue };
let name =
Name::parser
.parse(&name)
.map_err(|err| BridgeError::InvalidPackageName {
name: name.clone(),
error: err.into(),
})?;
if !names.contains(&name) {
return Err(BridgeError::UndeclaredPackageName(name.to_string()));
}
}
let packages = handle_packages(name, names, value.packages)?;
Ok(SourceInfoV1 { base, packages })
}
}
fn ensure_keyword_exists(
keyword: &Keyword,
map: &mut HashMap<Keyword, Value>,
) -> Result<Value, BridgeError> {
match map.remove(keyword) {
Some(value) => Ok(value),
None => Err(BridgeError::MissingRequiredKeyword {
keyword: keyword.clone(),
}),
}
}
fn ensure_no_suffix(
keyword: &Keyword,
architecture: Option<SystemArchitecture>,
) -> Result<(), BridgeError> {
if let Some(arch) = architecture {
return Err(BridgeError::UnexpectedArchitecture {
keyword: keyword.clone(),
suffix: arch,
});
}
Ok(())
}
fn ensure_single_value<'a>(keyword: &Keyword, value: &'a Value) -> Result<&'a String, BridgeError> {
match value {
Value::Single(item) => Ok(item),
Value::Array(values) => Err(BridgeError::UnexpectedArray {
keyword: keyword.clone(),
values: values.clone(),
}),
}
}
fn parse_value<'a, O, P: Parser<&'a str, O, ErrMode<ContextError>>>(
keyword: &Keyword,
value: &'a Value,
mut parser: P,
) -> Result<O, BridgeError> {
let input = ensure_single_value(keyword, value)?;
Ok(parser.parse(input).map_err(|err| (keyword.clone(), err))?)
}
fn parse_optional_value<'a, O, P: Parser<&'a str, O, ErrMode<ContextError>>>(
keyword: &Keyword,
value: &'a Value,
mut parser: P,
) -> Result<Option<O>, BridgeError> {
let input = ensure_single_value(keyword, value)?;
if input.trim().is_empty() {
return Ok(None);
}
Ok(Some(
parser.parse(input).map_err(|err| (keyword.clone(), err))?,
))
}
fn parse_value_array<'a, O, P: Parser<&'a str, O, ErrMode<ContextError>>>(
keyword: &Keyword,
value: &'a Value,
mut parser: P,
) -> Result<Vec<O>, BridgeError> {
let input = value.as_vec();
Ok(input
.into_iter()
.map(|item| parser.parse(item).map_err(|err| (keyword.clone(), err)))
.collect::<Result<Vec<O>, (Keyword, ParseError<&'a str, ContextError>)>>()?)
}
fn parse_arch_array<'a>(keyword: &Keyword, value: &'a Value) -> Result<Architectures, BridgeError> {
let input = value.as_vec();
if input.len() == 1 && input[0] == "any" {
return Ok(Architectures::Any);
}
let architectures = input
.into_iter()
.map(|item| {
SystemArchitecture::parser
.context(StrContext::Expected(StrContextValue::Description(
"either a single 'any' or an array of one or more specific system architectures."
)))
.parse(item)
.map_err(|err| (keyword.clone(), err))
})
.collect::<Result<Vec<SystemArchitecture>, (Keyword, ParseError<&'a str, ContextError>)>>()?;
Ok(Architectures::Some(architectures))
}
#[cfg(test)]
mod tests {
use testresult::TestResult;
use winnow::token::rest;
use super::*;
#[test]
pub fn test_empty_optional_value() -> TestResult {
let keyword = Keyword::simple("test");
let value = Value::Single("".to_string());
let value = parse_optional_value(&keyword, &value, rest)?;
assert!(value.is_none(), "Empty string values should return `None`.");
Ok(())
}
}