1use std::str::FromStr;
6
7use alpm_parsers::{iter_str_context, prelude::*};
8use alpm_types::{
9 Architecture,
10 BuildDate,
11 ExtraData,
12 ExtraDataEntry,
13 FullVersion,
14 Group,
15 InstalledSize,
16 License,
17 Name,
18 OptionalDependency,
19 PackageBaseName,
20 PackageDescription,
21 PackageInstallReason,
22 PackageRelation,
23 PackageValidation,
24 Packager,
25 RelationOrSoname,
26 Url,
27};
28use strum::{Display, EnumString, VariantNames};
29use winnow::{
30 ascii::{line_ending, newline, space0, till_line_ending},
31 combinator::{
32 alt,
33 cut_err,
34 delimited,
35 eof,
36 not,
37 opt,
38 peek,
39 preceded,
40 repeat,
41 repeat_till,
42 terminated,
43 },
44 error::ErrMode,
45 token::take_while,
46};
47
48#[derive(Clone, Debug, Display, EnumString, Eq, Hash, PartialEq, VariantNames)]
54#[strum(serialize_all = "UPPERCASE")]
55pub enum SectionKeyword {
56 Name,
58 Version,
60 Base,
62 Desc,
64 Url,
66 Arch,
68 BuildDate,
70 InstallDate,
72 Packager,
74 Size,
76 Groups,
78 Reason,
80 License,
82 Validation,
84 Replaces,
86 Depends,
88 OptDepends,
90 Conflicts,
92 Provides,
94 XData,
96}
97
98impl SectionKeyword {
99 pub fn parser<'a>(input: &mut Input<'a>) -> PResult<'a, Self> {
120 let section = delimited("%", take_while(1.., |c| c != '%'), "%");
121 terminated(
122 preceded(space0, section.try_map(Self::from_str)),
123 line_ending,
124 )
125 .parse_next(input)
126 }
127}
128
129#[derive(Clone, Debug)]
131pub enum Section {
132 Name(Name),
134 Version(FullVersion),
136 Base(PackageBaseName),
138 Desc(PackageDescription),
140 Url(Option<Url>),
142 Arch(Architecture),
144 BuildDate(BuildDate),
146 InstallDate(BuildDate),
148 Packager(Packager),
150 Size(InstalledSize),
152 Groups(Vec<Group>),
154 Reason(PackageInstallReason),
156 License(Vec<License>),
158 Validation(Vec<PackageValidation>),
160 Replaces(Vec<PackageRelation>),
162 Depends(Vec<RelationOrSoname>),
164 OptDepends(Vec<OptionalDependency>),
166 Conflicts(Vec<PackageRelation>),
168 Provides(Vec<RelationOrSoname>),
170 XData(ExtraData),
172}
173
174fn newlines<'a>(input: &mut Input<'a>) -> PResult<'a, ()> {
178 repeat(0.., line_ending).parse_next(input)
179}
180
181fn end_of_values<'a>(input: &mut Input<'a>) -> PResult<'a, ()> {
185 peek(alt((
186 line_ending.map(|_| ()),
187 SectionKeyword::parser.map(|_| ()),
188 eof.map(|_| ()),
189 )))
190 .parse_next(input)
191}
192
193fn try_value_from<'a, T>(layer: &str) -> impl Parser<Input<'a>, T, ErrMode<ParseStack<'a>>>
204where
205 T: FromStr,
206 T::Err: std::error::Error + Send + Sync + 'static,
207{
208 till_line_ending.try_map(T::from_str).layer(layer)
209}
210
211fn try_values_from<'a, T>(layer: &str) -> impl Parser<Input<'a>, Vec<T>, ErrMode<ParseStack<'a>>>
223where
224 T: FromStr,
225 T::Err: std::error::Error + Send + Sync + 'static,
226{
227 move |input: &mut Input<'a>| -> PResult<'a, Vec<T>> {
228 repeat_till(
229 0..,
230 terminated(
232 try_value_from(layer),
233 opt(terminated(line_ending, not(end_of_values))),
234 ),
235 end_of_values,
236 )
237 .map(|(outs, _)| outs)
238 .parse_next(input)
239 }
240}
241
242fn values<'a, T, P>(
254 type_parser: P,
255 layer: &str,
256) -> impl Parser<Input<'a>, Vec<T>, ErrMode<ParseStack<'a>>>
257where
258 P: Parser<Input<'a>, T, ErrMode<ParseStack<'a>>>,
259{
260 let mut type_parser = type_parser;
261 let parser = move |input: &mut Input<'a>| -> PResult<'a, Vec<T>> {
262 repeat_till(
263 0..,
264 terminated(
266 type_parser.by_ref(),
267 opt(terminated(line_ending, not(end_of_values))),
268 ),
269 end_of_values,
270 )
271 .map(|(outs, _)| outs)
272 .parse_next(input)
273 };
274
275 parser.layer(layer)
276}
277
278fn section<'a>(input: &mut Input<'a>) -> PResult<'a, Section> {
288 let section_keyword = cut_err(SectionKeyword::parser)
290 .description("expected a valid section name that is enclosed in `%` characters.")
291 .context_with(iter_str_context!([SectionKeyword::VARIANTS]))
292 .layer("section header")
293 .parse_next(input)?;
294
295 let section = match section_keyword {
298 SectionKeyword::Name => Section::Name(Name::parser_until_line_ending(input)?),
299 SectionKeyword::Version => Section::Version(FullVersion::parser_until_line_ending(input)?),
300 SectionKeyword::Base => Section::Base(PackageBaseName::parser_until_line_ending(input)?),
301 SectionKeyword::Desc => Section::Desc(
302 till_line_ending
303 .map(PackageDescription::new)
304 .parse_next(input)?,
305 ),
306 SectionKeyword::Url => Section::Url(
307 alt((
308 till_line_ending
310 .verify(|s: &str| s.trim().is_empty())
311 .map(|_s: &str| None),
312 till_line_ending
314 .try_map(Url::from_str)
315 .map(Some)
316 .layer("url field"),
317 ))
318 .parse_next(input)?,
319 ),
320 SectionKeyword::Arch => Section::Arch(Architecture::parser_until_line_ending(input)?),
321 SectionKeyword::BuildDate => {
322 Section::BuildDate(try_value_from("build date").parse_next(input)?)
323 }
324 SectionKeyword::InstallDate => {
325 Section::InstallDate(try_value_from("install date").parse_next(input)?)
326 }
327 SectionKeyword::Packager => Section::Packager(Packager::parser_until_line_ending(input)?),
328 SectionKeyword::Size => Section::Size(try_value_from("size").parse_next(input)?),
329 SectionKeyword::Groups => Section::Groups(try_values_from("group").parse_next(input)?),
330 SectionKeyword::Reason => {
331 Section::Reason(try_value_from("install reason").parse_next(input)?)
332 }
333 SectionKeyword::License => Section::License(try_values_from("license").parse_next(input)?),
334 SectionKeyword::Validation => Section::Validation(
335 values(PackageValidation::parser_until_line_ending, "validation").parse_next(input)?,
336 ),
337 SectionKeyword::Replaces => Section::Replaces(
338 values(PackageRelation::parser_until_line_ending, "replaces").parse_next(input)?,
339 ),
340 SectionKeyword::Depends => Section::Depends(
341 values(RelationOrSoname::parser_until_line_ending, "depends").parse_next(input)?,
342 ),
343 SectionKeyword::OptDepends => Section::OptDepends(
344 values(OptionalDependency::parser_until_line_ending, "optdepends").parse_next(input)?,
345 ),
346 SectionKeyword::Conflicts => Section::Conflicts(
347 values(PackageRelation::parser_until_line_ending, "conflicts").parse_next(input)?,
348 ),
349 SectionKeyword::Provides => Section::Provides(
350 values(RelationOrSoname::parser_until_line_ending, "provides").parse_next(input)?,
351 ),
352 SectionKeyword::XData => Section::XData(
353 values(ExtraDataEntry::parser_until_line_ending, "xdata")
354 .try_map(ExtraData::try_from)
355 .parse_next(input)?,
356 ),
357 };
358
359 alt((line_ending, eof)).parse_next(input)?;
361
362 Ok(section)
363}
364
365pub(crate) fn sections<'a>(input: &mut Input<'a>) -> PResult<'a, Vec<Section>> {
377 cut_err(repeat_till(
378 0..,
379 preceded(
380 opt(newline),
381 section.layer("alpm-repo-desc file value section"),
382 ),
383 terminated(opt(newlines), eof),
384 ))
385 .parse_next(input)
386 .map(|(sections, _)| sections)
387}