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