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