alpm_srcinfo/pkgbuild_bridge/
mod.rs1pub mod error;
4mod package;
5mod package_base;
6
7use std::collections::HashMap;
8
9use alpm_parsers::traits::AlpmParser;
10use alpm_pkgbuild::bridge::{BridgeOutput, Keyword, Value};
11use alpm_types::{Architectures, Name, SystemArchitecture};
12use package::handle_packages;
13use package_base::handle_package_base;
14use winnow::{
15 Parser,
16 error::{ContextError, ErrMode, ParseError, StrContext, StrContextValue},
17};
18
19use crate::{SourceInfoV1, pkgbuild_bridge::error::BridgeError};
20
21impl TryFrom<BridgeOutput> for SourceInfoV1 {
22 type Error = BridgeError;
23
24 fn try_from(mut value: BridgeOutput) -> Result<Self, Self::Error> {
28 let mut name = None;
29 let pkgbase_keyword = Keyword::simple("pkgbase");
31 if let Some(value) = value.package_base.remove(&pkgbase_keyword) {
32 name = Some(parse_value(&pkgbase_keyword, &value, Name::parser)?);
33 }
34
35 let pkgname_keyword = Keyword::simple("pkgname");
37 let names = ensure_keyword_exists(&pkgname_keyword, &mut value.package_base)?;
38 let names = parse_value_array(&pkgname_keyword, &names, Name::parser)?;
39
40 let name = match name {
42 Some(name) => name,
43 None => {
44 names.first().cloned().ok_or(BridgeError::NoName)?
46 }
47 };
48
49 let base = handle_package_base(name.clone(), value.package_base)?;
50
51 for name in value.functions {
54 let Some(name) = name.0 else { continue };
55
56 let name =
57 Name::parser
58 .parse(&name)
59 .map_err(|err| BridgeError::InvalidPackageName {
60 name: name.clone(),
61 error: err.into(),
62 })?;
63
64 if !names.contains(&name) {
65 return Err(BridgeError::UndeclaredPackageName(name.to_string()));
66 }
67 }
68
69 let packages = handle_packages(name, names, value.packages)?;
70
71 Ok(SourceInfoV1 { base, packages })
72 }
73}
74
75fn ensure_keyword_exists(
84 keyword: &Keyword,
85 map: &mut HashMap<Keyword, Value>,
86) -> Result<Value, BridgeError> {
87 match map.remove(keyword) {
88 Some(value) => Ok(value),
89 None => Err(BridgeError::MissingRequiredKeyword {
90 keyword: keyword.clone(),
91 }),
92 }
93}
94
95fn ensure_no_suffix(
102 keyword: &Keyword,
103 architecture: Option<SystemArchitecture>,
104) -> Result<(), BridgeError> {
105 if let Some(arch) = architecture {
106 return Err(BridgeError::UnexpectedArchitecture {
107 keyword: keyword.clone(),
108 suffix: arch,
109 });
110 }
111
112 Ok(())
113}
114
115fn ensure_single_value<'a>(keyword: &Keyword, value: &'a Value) -> Result<&'a String, BridgeError> {
122 match value {
123 Value::Single(item) => Ok(item),
124 Value::Array(values) => Err(BridgeError::UnexpectedArray {
125 keyword: keyword.clone(),
126 values: values.clone(),
127 }),
128 }
129}
130
131fn parse_value<'a, O, P: Parser<&'a str, O, ErrMode<ContextError>>>(
139 keyword: &Keyword,
140 value: &'a Value,
141 mut parser: P,
142) -> Result<O, BridgeError> {
143 let input = ensure_single_value(keyword, value)?;
144 Ok(parser.parse(input).map_err(|err| (keyword.clone(), err))?)
145}
146
147fn parse_optional_value<'a, O, P: Parser<&'a str, O, ErrMode<ContextError>>>(
157 keyword: &Keyword,
158 value: &'a Value,
159 mut parser: P,
160) -> Result<Option<O>, BridgeError> {
161 let input = ensure_single_value(keyword, value)?;
162
163 if input.trim().is_empty() {
164 return Ok(None);
165 }
166
167 Ok(Some(
168 parser.parse(input).map_err(|err| (keyword.clone(), err))?,
169 ))
170}
171
172fn parse_value_array<'a, O, P: Parser<&'a str, O, ErrMode<ContextError>>>(
183 keyword: &Keyword,
184 value: &'a Value,
185 mut parser: P,
186) -> Result<Vec<O>, BridgeError> {
187 let input = value.as_vec();
188 Ok(input
189 .into_iter()
190 .map(|item| parser.parse(item).map_err(|err| (keyword.clone(), err)))
191 .collect::<Result<Vec<O>, (Keyword, ParseError<&'a str, ContextError>)>>()?)
192}
193
194fn parse_arch_array<'a>(keyword: &Keyword, value: &'a Value) -> Result<Architectures, BridgeError> {
201 let input = value.as_vec();
203
204 if input.len() == 1 && input[0] == "any" {
209 return Ok(Architectures::Any);
210 }
211
212 let architectures = input
213 .into_iter()
214 .map(|item| {
215 SystemArchitecture::parser
216 .context(StrContext::Expected(StrContextValue::Description(
217 "either a single 'any' or an array of one or more specific system architectures."
218 )))
219 .parse(item)
220 .map_err(|err| (keyword.clone(), err))
221 })
222 .collect::<Result<Vec<SystemArchitecture>, (Keyword, ParseError<&'a str, ContextError>)>>()?;
223
224 Ok(Architectures::Some(architectures))
225}
226
227#[cfg(test)]
228mod tests {
229 use testresult::TestResult;
230 use winnow::token::rest;
231
232 use super::*;
233
234 #[test]
237 pub fn test_empty_optional_value() -> TestResult {
238 let keyword = Keyword::simple("test");
239 let value = Value::Single("".to_string());
240
241 let value = parse_optional_value(&keyword, &value, rest)?;
242
243 assert!(value.is_none(), "Empty string values should return `None`.");
244
245 Ok(())
246 }
247}