arch_pkg_text/parse/
desc.rs

1use 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        /// Parsed data of a `desc` file text.
13        ///
14        /// Every function call in [`Query`] and [`QueryMut`] is constant time.
15        #[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            /// Get a raw value from the querier.
24            fn get_raw_value(&self, field_name: FieldName) -> Option<&'a str> {
25                match field_name {$(
26                    FieldName::$field => self.$field,
27                )*}
28            }
29
30            /// Add a raw value into the querier.
31            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/// Error type of [`ParsedDesc::parse`].
49#[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/// Issue that may arise during parsing.
58#[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    /// Return `Ok(())` if the issue was [`DescParseIssue::UnknownField`],
67    /// or return an `Err` of [`DescParseError`] otherwise.
68    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    /// Parse a `desc` file text, unknown fields are ignored.
81    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    /// Parse a `desc` file text with a callback that handle [parsing issues](DescParseIssue).
87    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        // parse the first field
108        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        // parse the remaining values and fields.
122        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    /// Parse a value until the end of input or when a [`RawField`] is found.
141    ///
142    /// This function returns a tuple of the length of the value and the next field.
143    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}