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.filter(|value| !value.is_empty()),
30 )*}
31 }
32
33 fn set_raw_value(&mut self, field_name: FieldName, raw_value: &'a str) {
35 match field_name {$(
36 FieldName::$field => if self.$field.is_none() {
37 self.$field = Some(raw_value);
38 },
39 )*}
40 }
41 }
42 };
43}
44
45def_struct!(
46 FileName Name Base Version Description Groups
47 CompressedSize InstalledSize Md5Checksum Sha256Checksum
48 PgpSignature Url License Architecture BuildDate Packager
49 Dependencies CheckDependencies MakeDependencies OptionalDependencies
50 Provides Conflicts Replaces
51);
52
53#[derive(Debug, Display, Error, Clone, Copy)]
55pub enum DescParseError<'a> {
56 #[display("Input has no field")]
57 NoField,
58 #[display("Receive a value without field: {_0:?}")]
59 ValueWithoutField(#[error(not(source))] &'a str),
60}
61
62#[derive(Debug, Clone, Copy)]
64pub enum DescParseIssue<'a> {
65 NoField,
66 FirstLineIsNotAField(&'a str, ParseRawFieldError),
67 UnknownField(RawField<'a>),
68}
69
70impl<'a> DescParseIssue<'a> {
71 pub fn ignore_unknown_field(self) -> Result<(), DescParseError<'a>> {
76 Err(match self {
77 DescParseIssue::NoField => DescParseError::NoField,
78 DescParseIssue::FirstLineIsNotAField(line, _) => {
79 DescParseError::ValueWithoutField(line)
80 }
81 DescParseIssue::UnknownField(_) => return Ok(()),
82 })
83 }
84}
85
86impl<'a> ParsedDesc<'a> {
87 pub fn parse(text: &'a str) -> Result<Self, DescParseError<'a>> {
89 ParsedDesc::partial_parse(text).try_into_complete()
90 }
91
92 pub fn parse_with_issues<HandleIssue, Error>(
94 text: &'a str,
95 mut handle_issue: HandleIssue,
96 ) -> PartialParseResult<ParsedDesc<'a>, Error>
97 where
98 HandleIssue: FnMut(DescParseIssue<'a>) -> Result<(), Error>,
99 {
100 let mut parsed = ParsedDesc::default();
101 let mut lines = text.lines_inclusive();
102
103 macro_rules! return_or {
104 ($issue:expr, $alternative:expr) => {
105 match handle_issue($issue) {
106 Err(error) => return PartialParseResult::new_partial(parsed, error),
107 Ok(()) => $alternative,
108 }
109 };
110 }
111
112 let (first_line, first_field) = loop {
114 let Some(first_line) = lines.next() else {
115 return_or!(
116 DescParseIssue::NoField,
117 return PartialParseResult::new_complete(parsed)
118 );
119 };
120 let first_field = match first_line.trim().pipe(RawField::parse_raw) {
121 Ok(first_field) => first_field,
122 Err(error) => {
123 return_or!(
124 DescParseIssue::FirstLineIsNotAField(first_line, error),
125 continue
126 )
127 }
128 };
129 break (first_line, first_field);
130 };
131
132 let mut current_field = Some((first_field, first_line));
134 while let Some((field, field_line)) = current_field {
135 let (value_length, next_field) = ParsedDesc::parse_next(&mut lines);
136 let value_start_offset =
137 field_line.as_ptr() as usize + field_line.len() - text.as_ptr() as usize;
138 let value_end_offset = value_start_offset + value_length;
139 if let Ok(field) = field.to_parsed::<FieldName>() {
140 let value = text[value_start_offset..value_end_offset].trim();
141 parsed.set_raw_value(*field.name(), value);
142 } else {
143 return_or!(DescParseIssue::UnknownField(field), ())
144 }
145 current_field = next_field;
146 }
147
148 PartialParseResult::new_complete(parsed)
149 }
150
151 fn parse_next(
155 remaining_lines: &mut LinesInclusiveIter<'a>,
156 ) -> (usize, Option<(RawField<'a>, &'a str)>) {
157 let mut value_length = 0;
158
159 for line in remaining_lines {
160 if let Ok(field) = line.trim().pipe(RawField::parse_raw) {
161 return (value_length, Some((field, line)));
162 }
163 value_length += line.len();
164 }
165
166 (value_length, None)
167 }
168}
169
170impl<'a> TryFrom<&'a str> for ParsedDesc<'a> {
172 type Error = DescParseError<'a>;
174 fn try_from(text: &'a str) -> Result<Self, Self::Error> {
176 ParsedDesc::parse(text)
177 }
178}
179
180impl<'a> PartialParse<&'a str> for ParsedDesc<'a> {
181 type Error = DescParseError<'a>;
182 fn partial_parse(input: &'a str) -> PartialParseResult<Self, Self::Error> {
183 ParsedDesc::parse_with_issues(input, DescParseIssue::ignore_unknown_field)
184 }
185}
186
187impl<'a, HandleIssue, Error> ParseWithIssues<&'a str, HandleIssue, Error> for ParsedDesc<'a>
188where
189 HandleIssue: FnMut(DescParseIssue<'a>) -> Result<(), Error>,
190{
191 fn parse_with_issues(
192 input: &'a str,
193 handle_issue: HandleIssue,
194 ) -> PartialParseResult<Self, Error> {
195 ParsedDesc::parse_with_issues(input, handle_issue)
196 }
197}
198
199impl<'a> Query<'a> for ParsedDesc<'a> {
200 fn query_raw_text(&self, field: ParsedField) -> Option<&'a str> {
201 self.get_raw_value(*field.name())
202 }
203}
204
205impl<'a> QueryMut<'a> for ParsedDesc<'a> {
206 fn query_raw_text_mut(&mut self, field: ParsedField) -> Option<&'a str> {
207 self.query_raw_text(field)
208 }
209}
210
211impl ReuseAdvice for ParsedDesc<'_> {
212 type ShouldReuse = True;
217}