alpm_srcinfo/pkgbuild_bridge/
mod.rs1pub mod error;
4mod package;
5mod package_base;
6
7use std::collections::HashMap;
8
9use alpm_pkgbuild::bridge::{BridgeOutput, Keyword, Value};
10use alpm_types::{Architecture, Name};
11use package::handle_packages;
12use package_base::handle_package_base;
13use winnow::{
14 Parser,
15 error::{ContextError, ErrMode, ParseError},
16};
17
18use crate::{SourceInfoV1, pkgbuild_bridge::error::BridgeError};
19
20impl TryFrom<BridgeOutput> for SourceInfoV1 {
21 type Error = BridgeError;
22
23 fn try_from(mut value: BridgeOutput) -> Result<Self, Self::Error> {
27 let mut name = None;
28 let pkgbase_keyword = Keyword::simple("pkgbase");
30 if let Some(value) = value.package_base.remove(&pkgbase_keyword) {
31 name = Some(parse_value(&pkgbase_keyword, &value, Name::parser)?);
32 }
33
34 let pkgname_keyword = Keyword::simple("pkgname");
36 let names = ensure_keyword_exists(&pkgname_keyword, &mut value.package_base)?;
37 let names = parse_value_array(&pkgname_keyword, &names, Name::parser)?;
38
39 let name = match name {
41 Some(name) => name,
42 None => {
43 names.first().cloned().ok_or(BridgeError::NoName)?
45 }
46 };
47
48 let base = handle_package_base(name.clone(), value.package_base)?;
49
50 for name in value.functions {
53 let Some(name) = name.0 else { continue };
54
55 let name =
56 Name::parser
57 .parse(&name)
58 .map_err(|err| BridgeError::InvalidPackageName {
59 name: name.clone(),
60 error: err.into(),
61 })?;
62
63 if !names.contains(&name) {
64 return Err(BridgeError::UndeclaredPackageName(name.to_string()));
65 }
66 }
67
68 let packages = handle_packages(name, names, value.packages)?;
69
70 Ok(SourceInfoV1 { base, packages })
71 }
72}
73
74fn ensure_keyword_exists(
83 keyword: &Keyword,
84 map: &mut HashMap<Keyword, Value>,
85) -> Result<Value, BridgeError> {
86 match map.remove(keyword) {
87 Some(value) => Ok(value),
88 None => Err(BridgeError::MissingRequiredKeyword {
89 keyword: keyword.clone(),
90 }),
91 }
92}
93
94fn ensure_no_suffix(
101 keyword: &Keyword,
102 architecture: Option<Architecture>,
103) -> Result<(), BridgeError> {
104 if let Some(arch) = architecture {
105 return Err(BridgeError::UnexpectedArchitecture {
106 keyword: keyword.clone(),
107 suffix: arch,
108 });
109 }
110
111 Ok(())
112}
113
114fn ensure_single_value<'a>(keyword: &Keyword, value: &'a Value) -> Result<&'a String, BridgeError> {
121 match value {
122 Value::Single(item) => Ok(item),
123 Value::Array(values) => Err(BridgeError::UnexpectedArray {
124 keyword: keyword.clone(),
125 values: values.clone(),
126 }),
127 }
128}
129
130fn parse_value<'a, O, P: Parser<&'a str, O, ErrMode<ContextError>>>(
138 keyword: &Keyword,
139 value: &'a Value,
140 mut parser: P,
141) -> Result<O, BridgeError> {
142 let input = ensure_single_value(keyword, value)?;
143 Ok(parser.parse(input).map_err(|err| (keyword.clone(), err))?)
144}
145
146fn parse_optional_value<'a, O, P: Parser<&'a str, O, ErrMode<ContextError>>>(
156 keyword: &Keyword,
157 value: &'a Value,
158 mut parser: P,
159) -> Result<Option<O>, BridgeError> {
160 let input = ensure_single_value(keyword, value)?;
161
162 if input.trim().is_empty() {
163 return Ok(None);
164 }
165
166 Ok(Some(
167 parser.parse(input).map_err(|err| (keyword.clone(), err))?,
168 ))
169}
170
171fn parse_value_array<'a, O, P: Parser<&'a str, O, ErrMode<ContextError>>>(
182 keyword: &Keyword,
183 value: &'a Value,
184 mut parser: P,
185) -> Result<Vec<O>, BridgeError> {
186 let input = value.as_vec();
187 Ok(input
188 .into_iter()
189 .map(|item| parser.parse(item).map_err(|err| (keyword.clone(), err)))
190 .collect::<Result<Vec<O>, (Keyword, ParseError<&'a str, ContextError>)>>()?)
191}
192
193#[cfg(test)]
194mod tests {
195 use testresult::TestResult;
196 use winnow::token::rest;
197
198 use super::*;
199
200 #[test]
203 pub fn test_empty_optional_value() -> TestResult {
204 let keyword = Keyword::simple("test");
205 let value = Value::Single("".to_string());
206
207 let value = parse_optional_value(&keyword, &value, rest)?;
208
209 assert!(value.is_none(), "Empty string values should return `None`.");
210
211 Ok(())
212 }
213}