arch_pkg_text/parse/
desc.rs1use super::{ParseWithIssues, PartialParse, PartialParseResult};
2use crate::desc::{
3 FieldName, ParseRawFieldError, ParsedField, Query, QueryMut, RawField,
4 misc::{ReuseAdvice, True},
5};
6use derive_more::{Display, Error};
7use lines_inclusive::{LinesInclusive, LinesInclusiveIter};
8use pipe_trait::Pipe;
9
10macro_rules! def_struct {
11 ($(
12 $(#[$attrs:meta])*
13 $field:ident $(,)? $(;)?
14 )*) => {
15 #[derive(Debug, Default, Clone, Copy)]
19 #[allow(non_snake_case, reason = "We don't access the field names directly, keep it simple.")]
20 pub struct ParsedDesc<'a> {$(
21 $(#[$attrs])*
22 $field: Option<&'a str>,
23 )*}
24
25 impl<'a> ParsedDesc<'a> {
26 fn get_raw_value(&self, field_name: FieldName) -> Option<&'a str> {
28 match field_name {$(
29 FieldName::$field => self.$field,
30 )*}
31 }
32
33 fn set_raw_value(&mut self, field_name: FieldName, raw_value: &'a str) {
35 match field_name {$(
36 FieldName::$field => self.$field = Some(raw_value),
37 )*}
38 }
39 }
40 };
41}
42
43def_struct!(
44 FileName Name Base Version Description Groups
45 CompressedSize InstalledSize Md5Checksum Sha256Checksum
46 PgpSignature Url License Architecture BuildDate Packager
47 Dependencies CheckDependencies MakeDependencies OptionalDependencies
48 Provides Conflicts Replaces
49);
50
51#[derive(Debug, Display, Error, Clone, Copy)]
53pub enum DescParseError<'a> {
54 #[display("Input is empty")]
55 EmptyInput,
56 #[display("Receive a value without field: {_0:?}")]
57 ValueWithoutField(#[error(not(source))] &'a str),
58}
59
60#[derive(Debug, Clone, Copy)]
62pub enum DescParseIssue<'a> {
63 EmptyInput,
64 FirstLineIsNotAField(&'a str, ParseRawFieldError),
65 UnknownField(RawField<'a>),
66}
67
68impl<'a> DescParseIssue<'a> {
69 pub fn ignore_unknown_field(self) -> Result<(), DescParseError<'a>> {
74 Err(match self {
75 DescParseIssue::EmptyInput => DescParseError::EmptyInput,
76 DescParseIssue::FirstLineIsNotAField(line, _) => {
77 DescParseError::ValueWithoutField(line)
78 }
79 DescParseIssue::UnknownField(_) => return Ok(()),
80 })
81 }
82}
83
84impl<'a> ParsedDesc<'a> {
85 pub fn parse(text: &'a str) -> Result<Self, DescParseError<'a>> {
87 ParsedDesc::partial_parse(text).try_into_complete()
88 }
89
90 pub fn parse_with_issues<HandleIssue, Error>(
92 text: &'a str,
93 mut handle_issue: HandleIssue,
94 ) -> PartialParseResult<ParsedDesc<'a>, Error>
95 where
96 HandleIssue: FnMut(DescParseIssue<'a>) -> Result<(), Error>,
97 {
98 let mut parsed = ParsedDesc::default();
99 let mut lines = text.lines_inclusive();
100 let mut processed_length = 0;
101
102 macro_rules! return_or {
103 ($issue:expr, $alternative:expr) => {
104 match handle_issue($issue) {
105 Err(error) => return PartialParseResult::new_partial(parsed, error),
106 Ok(()) => $alternative,
107 }
108 };
109 }
110
111 let (first_line, first_field) = loop {
113 let Some(first_line) = lines.next() else {
114 return_or!(DescParseIssue::EmptyInput, continue);
115 };
116 let first_field = match first_line.trim().pipe(RawField::parse_raw) {
117 Ok(first_field) => first_field,
118 Err(error) => {
119 return_or!(
120 DescParseIssue::FirstLineIsNotAField(first_line, error),
121 continue
122 )
123 }
124 };
125 break (first_line, first_field);
126 };
127
128 let mut current_field = Some((first_field, first_line));
130 while let Some((field, field_line)) = current_field {
131 let (value_length, next_field) = ParsedDesc::parse_next(&mut lines);
132 let value_start_offset = processed_length + field_line.len();
133 let value_end_offset = value_start_offset + value_length;
134 if let Ok(field) = field.to_parsed::<FieldName>() {
135 let value = text[value_start_offset..value_end_offset].trim();
136 parsed.set_raw_value(*field.name(), value);
137 } else {
138 return_or!(DescParseIssue::UnknownField(field), ())
139 }
140 processed_length = value_end_offset;
141 current_field = next_field;
142 }
143
144 PartialParseResult::new_complete(parsed)
145 }
146
147 fn parse_next(
151 remaining_lines: &mut LinesInclusiveIter<'a>,
152 ) -> (usize, Option<(RawField<'a>, &'a str)>) {
153 let mut value_length = 0;
154
155 for line in remaining_lines {
156 if let Ok(field) = line.trim().pipe(RawField::parse_raw) {
157 return (value_length, Some((field, line)));
158 }
159 value_length += line.len();
160 }
161
162 (value_length, None)
163 }
164}
165
166impl<'a> TryFrom<&'a str> for ParsedDesc<'a> {
168 type Error = DescParseError<'a>;
170 fn try_from(text: &'a str) -> Result<Self, Self::Error> {
172 ParsedDesc::parse(text)
173 }
174}
175
176impl<'a> PartialParse<&'a str> for ParsedDesc<'a> {
177 type Error = DescParseError<'a>;
178 fn partial_parse(input: &'a str) -> PartialParseResult<Self, Self::Error> {
179 ParsedDesc::parse_with_issues(input, DescParseIssue::ignore_unknown_field)
180 }
181}
182
183impl<'a, HandleIssue, Error> ParseWithIssues<&'a str, HandleIssue, Error> for ParsedDesc<'a>
184where
185 HandleIssue: FnMut(DescParseIssue<'a>) -> Result<(), Error>,
186{
187 fn parse_with_issues(
188 input: &'a str,
189 handle_issue: HandleIssue,
190 ) -> PartialParseResult<Self, Error> {
191 ParsedDesc::parse_with_issues(input, handle_issue)
192 }
193}
194
195impl<'a> Query<'a> for ParsedDesc<'a> {
196 fn query_raw_text(&self, field: ParsedField) -> Option<&'a str> {
197 self.get_raw_value(*field.name())
198 }
199}
200
201impl<'a> QueryMut<'a> for ParsedDesc<'a> {
202 fn query_raw_text_mut(&mut self, field: ParsedField) -> Option<&'a str> {
203 self.query_raw_text(field)
204 }
205}
206
207impl ReuseAdvice for ParsedDesc<'_> {
208 type ShouldReuse = True;
213}