arch_pkg_text/parse/
desc.rs

1use super::PartialParseResult;
2use crate::desc::{
3    misc::{ReuseAdvice, True},
4    FieldName, ParseRawFieldError, ParsedField, Query, QueryMut, RawField,
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        /// Parsed data of a `desc` file text.
16        ///
17        /// Every function call in [`Query`] and [`QueryMut`] is constant time.
18        #[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            /// Get a raw value from the querier.
27            fn get_raw_value(&self, field_name: FieldName) -> Option<&'a str> {
28                match field_name {$(
29                    FieldName::$field => self.$field,
30                )*}
31            }
32
33            /// Add a raw value into the querier.
34            fn set_raw_value(&mut self, field_name: FieldName, raw_value: &'a str) {
35                match field_name {$(
36                    FieldName::$field => self.$field = Some(raw_value),
37                )*}
38            }
39        }
40    };
41}
42
43def_struct!(
44    FileName Name Base Version Description Groups
45    CompressedSize InstalledSize Md5Checksum Sha256Checksum
46    PgpSignature Url License Architecture BuildDate Packager
47    Dependencies CheckDependencies MakeDependencies OptionalDependencies
48    Provides Conflicts Replaces
49);
50
51/// Error type of [`ParsedDesc::parse`].
52#[derive(Debug, Display, Error, Clone, Copy)]
53pub enum DescParseError<'a> {
54    #[display("Input is empty")]
55    EmptyInput,
56    #[display("Receive a value without field: {_0:?}")]
57    ValueWithoutField(#[error(not(source))] &'a str),
58}
59
60/// Issue that may arise during parsing.
61#[derive(Debug, Clone, Copy)]
62pub enum DescParseIssue<'a> {
63    EmptyInput,
64    FirstLineIsNotAField(&'a str, ParseRawFieldError),
65    UnknownField(RawField<'a>),
66}
67
68impl<'a> DescParseIssue<'a> {
69    /// Return `Ok(())` if the issue was [`DescParseIssue::UnknownField`],
70    /// or return an `Err` of [`DescParseError`] otherwise.
71    fn ignore_unknown_field(self) -> Result<(), DescParseError<'a>> {
72        Err(match self {
73            DescParseIssue::EmptyInput => DescParseError::EmptyInput,
74            DescParseIssue::FirstLineIsNotAField(line, _) => {
75                DescParseError::ValueWithoutField(line)
76            }
77            DescParseIssue::UnknownField(_) => return Ok(()),
78        })
79    }
80}
81
82impl<'a> ParsedDesc<'a> {
83    /// Parse a `desc` file text, unknown fields are ignored.
84    pub fn parse(text: &'a str) -> Result<Self, DescParseError<'a>> {
85        ParsedDesc::parse_with_issues(text, DescParseIssue::ignore_unknown_field)
86            .try_into_complete()
87    }
88
89    /// Parse a `desc` file text with a callback that handle [parsing issues](DescParseIssue).
90    pub fn parse_with_issues<HandleIssue, Error>(
91        text: &'a str,
92        mut handle_issue: HandleIssue,
93    ) -> PartialParseResult<ParsedDesc<'a>, Error>
94    where
95        HandleIssue: FnMut(DescParseIssue<'a>) -> Result<(), Error>,
96    {
97        let mut parsed = ParsedDesc::default();
98        let mut lines = text.lines_inclusive();
99        let mut processed_length = 0;
100
101        macro_rules! return_or_continue {
102            ($issue:expr) => {
103                match handle_issue($issue) {
104                    Err(error) => return PartialParseResult::new_partial(parsed, error),
105                    Ok(()) => continue,
106                }
107            };
108        }
109
110        // parse the first field
111        let (first_line, first_field) = loop {
112            let Some(first_line) = lines.next() else {
113                return_or_continue!(DescParseIssue::EmptyInput);
114            };
115            let first_field = match first_line.trim().pipe(RawField::parse_raw) {
116                Ok(first_field) => first_field,
117                Err(error) => {
118                    return_or_continue!(DescParseIssue::FirstLineIsNotAField(first_line, error))
119                }
120            };
121            break (first_line, first_field);
122        };
123
124        // parse the remaining values and fields.
125        let mut current_field = Some((first_field, first_line));
126        while let Some((field, field_line)) = current_field {
127            let (value_length, next_field) = ParsedDesc::parse_next(&mut lines);
128            let value_start_offset = processed_length + field_line.len();
129            let value_end_offset = value_start_offset + value_length;
130            if let Ok(field) = field.to_parsed::<FieldName>() {
131                let value = text[value_start_offset..value_end_offset].trim();
132                parsed.set_raw_value(*field.name(), value);
133            } else {
134                return_or_continue!(DescParseIssue::UnknownField(field));
135            }
136            processed_length = value_end_offset;
137            current_field = next_field;
138        }
139
140        PartialParseResult::new_complete(parsed)
141    }
142
143    /// Parse a value until the end of input or when a [`RawField`] is found.
144    ///
145    /// This function returns a tuple of the length of the value and the next field.
146    fn parse_next(
147        remaining_lines: &mut LinesInclusiveIter<'a>,
148    ) -> (usize, Option<(RawField<'a>, &'a str)>) {
149        let mut value_length = 0;
150
151        for line in remaining_lines {
152            if let Ok(field) = line.trim().pipe(RawField::parse_raw) {
153                return (value_length, Some((field, line)));
154            }
155            value_length += line.len();
156        }
157
158        (value_length, None)
159    }
160}
161
162impl<'a> TryFrom<&'a str> for ParsedDesc<'a> {
163    type Error = DescParseError<'a>;
164    fn try_from(text: &'a str) -> Result<Self, Self::Error> {
165        ParsedDesc::parse(text)
166    }
167}
168
169impl<'a> Query<'a> for ParsedDesc<'a> {
170    fn query_raw_text(&self, field: ParsedField) -> Option<&'a str> {
171        self.get_raw_value(*field.name())
172    }
173}
174
175impl<'a> QueryMut<'a> for ParsedDesc<'a> {
176    fn query_raw_text_mut(&mut self, field: ParsedField) -> Option<&'a str> {
177        self.query_raw_text(field)
178    }
179}
180
181impl ReuseAdvice for ParsedDesc<'_> {
182    /// [`ParsedDesc`] costs O(n) time to construct (n being text length).
183    /// Performing a lookup on it costs O(1) time.
184    ///
185    /// This struct is designed to be reused.
186    type ShouldReuse = True;
187}