arch_pkg_text/parse/
desc.rs

1use 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        /// Parsed data of a `desc` file text.
15        ///
16        /// Every function call in [`Query`] and [`QueryMut`] is constant time.
17        #[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            /// Get a raw value from the querier.
26            fn get_raw_value(&self, field_name: FieldName) -> Option<&'a str> {
27                match field_name {$(
28                    FieldName::$field => self.$field,
29                )*}
30            }
31
32            /// Add a raw value into the querier.
33            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/// Error type of [`ParsedDesc::parse`].
51#[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/// Issue that may arise during parsing.
60#[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    /// Return `Ok(())` if the issue was [`DescParseIssue::UnknownField`],
69    /// or return an `Err` of [`DescParseError`] otherwise.
70    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    /// Parse a `desc` file text, unknown fields are ignored.
83    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    /// Parse a `desc` file text with a callback that handle [parsing issues](DescParseIssue).
89    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        // parse the first field
110        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        // parse the remaining values and fields.
124        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    /// Parse a value until the end of input or when a [`RawField`] is found.
143    ///
144    /// This function returns a tuple of the length of the value and the next field.
145    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    /// [`ParsedDesc`] costs O(n) time to construct (n being text length).
182    /// Performing a lookup on it costs O(1) time.
183    ///
184    /// This struct is designed to be reused.
185    const ENCOURAGE_REUSE: bool = true;
186}