arch_pkg_text/parse/
srcinfo.rs

1mod checksums;
2mod data;
3
4use super::{ParseWithIssues, PartialParse, PartialParseResult};
5use crate::{
6    srcinfo::{
7        Field, FieldName, ParsedField, RawField, Section,
8        misc::{ReuseAdvice, True},
9        utils::{non_blank_trimmed_lines, parse_line},
10    },
11    value,
12};
13use derive_more::{Display, Error};
14use indexmap::IndexMap;
15use pipe_trait::Pipe;
16
17pub use data::{
18    ParsedSrcinfoBaseSection, ParsedSrcinfoBaseUniqueFieldDuplicationError,
19    ParsedSrcinfoDerivativeSection, ParsedSrcinfoDerivativeUniqueFieldDuplicationError,
20};
21
22/// Parsed information of `.SRCINFO`.
23#[derive(Debug, Default, Clone)]
24pub struct ParsedSrcinfo<'a> {
25    /// The section under `pkgbase`.
26    pub base: ParsedSrcinfoBaseSection<'a>,
27    /// The sections under `pkgname`.
28    pub derivatives: IndexMap<value::Name<'a>, ParsedSrcinfoDerivativeSection<'a>>,
29}
30
31/// Write cursor of the sections in [`ParsedSrcinfo`].
32enum ParsedSrcinfoSectionMut<'a, 'r> {
33    /// Write to the `pkgbase` section.
34    Base(&'r mut ParsedSrcinfoBaseSection<'a>),
35    /// Write to a `pkgname` section.
36    Derivative(ParsedSrcinfoDerivativeSectionEntryMut<'a, 'r>),
37}
38
39impl<'a> ParsedSrcinfo<'a> {
40    /// Get a section or create one if didn't exist.
41    fn get_or_insert(&mut self, section: Section<'a>) -> ParsedSrcinfoSectionMut<'a, '_> {
42        match section {
43            Section::Base => self.base.pipe_mut(ParsedSrcinfoSectionMut::Base),
44            Section::Derivative(name) => self
45                .derivatives
46                .entry(name)
47                .or_default()
48                .pipe(|data| ParsedSrcinfoDerivativeSectionEntryMut::new(name, data))
49                .pipe(ParsedSrcinfoSectionMut::Derivative),
50        }
51    }
52}
53
54/// Private error type for control flow.
55enum AddFailure<'a> {
56    /// Meet an entry with field `pkgname`.
57    MeetHeader(value::Name<'a>),
58    /// Meet an issue.
59    Issue(SrcinfoParseIssue<'a>),
60}
61
62/// Create an [`Err`] of an [`AddFailure::Issue`] of a [`SrcinfoParseIssue::UnknownField`] of a [`ParsedField`].
63fn unknown_field_from_parsed(field: ParsedField<&str>) -> Result<(), AddFailure<'_>> {
64    Field::blank()
65        .with_name(field.name_str())
66        .with_architecture(field.architecture_str())
67        .pipe(SrcinfoParseIssue::UnknownField)
68        .pipe(AddFailure::Issue)
69        .pipe(Err)
70}
71
72impl<'a> ParsedSrcinfoSectionMut<'a, '_> {
73    /// Add an entry to a `pkgbase` or `pkgname` section.
74    fn add(&mut self, field: ParsedField<&'a str>, value: &'a str) -> Result<(), AddFailure<'a>> {
75        match self {
76            ParsedSrcinfoSectionMut::Base(section) => section.add(field, value),
77            ParsedSrcinfoSectionMut::Derivative(section) => section.add(field, value),
78        }
79    }
80
81    /// Shrink all internal containers' capacities to fit.
82    fn shrink_to_fit(&mut self) {
83        match self {
84            ParsedSrcinfoSectionMut::Base(section) => section.shrink_to_fit(),
85            ParsedSrcinfoSectionMut::Derivative(section) => section.shrink_to_fit(),
86        }
87    }
88}
89
90impl<'a> ParsedSrcinfoBaseSection<'a> {
91    /// Add a value to a unique entry.
92    fn add_value_to_option<Value: Copy>(
93        target: &mut Option<Value>,
94        value: &'a str,
95        make_value: impl FnOnce(&'a str) -> Value,
96        make_error: impl FnOnce(Value) -> ParsedSrcinfoBaseUniqueFieldDuplicationError<'a>,
97    ) -> Result<(), AddFailure<'a>> {
98        let Some(old_value) = target else {
99            *target = Some(make_value(value));
100            return Ok(());
101        };
102        (*old_value)
103            .pipe(make_error)
104            .pipe(SrcinfoParseIssue::BaseUniqueFieldDuplication)
105            .pipe(AddFailure::Issue)
106            .pipe(Err)
107    }
108}
109
110/// A pair of [`value::Name`] and [`ParsedSrcinfoDerivativeSection`].
111struct ParsedSrcinfoDerivativeSectionEntryMut<'a, 'r> {
112    name: value::Name<'a>,
113    data: &'r mut ParsedSrcinfoDerivativeSection<'a>,
114}
115
116impl<'a, 'r> ParsedSrcinfoDerivativeSectionEntryMut<'a, 'r> {
117    /// Create a new pair.
118    fn new(name: value::Name<'a>, data: &'r mut ParsedSrcinfoDerivativeSection<'a>) -> Self {
119        ParsedSrcinfoDerivativeSectionEntryMut { name, data }
120    }
121
122    /// Add a value to a unique entry.
123    fn add_value_to_option<Value: Copy>(
124        name: value::Name<'a>,
125        target: &mut Option<Value>,
126        value: &'a str,
127        make_value: impl FnOnce(&'a str) -> Value,
128        make_error: impl FnOnce(Value) -> ParsedSrcinfoDerivativeUniqueFieldDuplicationError<'a>,
129    ) -> Result<(), AddFailure<'a>> {
130        let Some(old_value) = target else {
131            *target = Some(make_value(value));
132            return Ok(());
133        };
134        (*old_value)
135            .pipe(make_error)
136            .pipe(move |error| SrcinfoParseIssue::DerivativeUniqueFieldDuplication(name, error))
137            .pipe(AddFailure::Issue)
138            .pipe(Err)
139    }
140
141    /// Shrink all internal containers' capacities to fit.
142    fn shrink_to_fit(&mut self) {
143        self.data.shrink_to_fit()
144    }
145}
146
147/// Error type of [`ParsedSrcinfo::parse`].
148#[derive(Debug, Display, Error, Clone, Copy)]
149pub enum SrcinfoParseError<'a> {
150    #[display("Failed to insert value to the pkgbase section: {_0}")]
151    BaseUniqueFieldDuplication(
152        #[error(not(source))] ParsedSrcinfoBaseUniqueFieldDuplicationError<'a>,
153    ),
154    #[display("Failed to insert value to the pkgname section named {_0}: {_1}")]
155    DerivativeUniqueFieldDuplication(
156        value::Name<'a>,
157        ParsedSrcinfoDerivativeUniqueFieldDuplicationError<'a>,
158    ),
159    #[display("Invalid line: {_0:?}")]
160    InvalidLine(#[error(not(source))] &'a str),
161}
162
163/// Return type of [`ParsedSrcinfo::parse`].
164pub type SrcinfoParseReturn<'a> = PartialParseResult<ParsedSrcinfo<'a>, SrcinfoParseError<'a>>;
165
166/// Issue that may arise during parsing.
167#[derive(Debug, Clone, Copy)]
168pub enum SrcinfoParseIssue<'a> {
169    UnknownField(RawField<'a>),
170    BaseUniqueFieldDuplication(ParsedSrcinfoBaseUniqueFieldDuplicationError<'a>),
171    DerivativeUniqueFieldDuplication(
172        value::Name<'a>,
173        ParsedSrcinfoDerivativeUniqueFieldDuplicationError<'a>,
174    ),
175    InvalidLine(&'a str),
176}
177
178impl<'a> SrcinfoParseIssue<'a> {
179    /// Return `Ok(())` if the issue was [`SrcinfoParseIssue::UnknownField`],
180    /// or return an `Err` of [`SrcinfoParseError`] otherwise.
181    ///
182    /// This function is the default issue handler for [`ParsedSrcinfo`].
183    pub fn ignore_unknown_field(self) -> Result<(), SrcinfoParseError<'a>> {
184        Err(match self {
185            SrcinfoParseIssue::UnknownField(_) => return Ok(()),
186            SrcinfoParseIssue::BaseUniqueFieldDuplication(error) => {
187                SrcinfoParseError::BaseUniqueFieldDuplication(error)
188            }
189            SrcinfoParseIssue::DerivativeUniqueFieldDuplication(name, error) => {
190                SrcinfoParseError::DerivativeUniqueFieldDuplication(name, error)
191            }
192            SrcinfoParseIssue::InvalidLine(line) => SrcinfoParseError::InvalidLine(line),
193        })
194    }
195}
196
197impl<'a> ParsedSrcinfo<'a> {
198    /// Parse `.SRCINFO` text, [unknown fields are ignored](SrcinfoParseIssue::ignore_unknown_field).
199    pub fn parse(text: &'a str) -> SrcinfoParseReturn<'a> {
200        ParsedSrcinfo::parse_with_issues(text, SrcinfoParseIssue::ignore_unknown_field)
201    }
202
203    /// Parse `.SRCINFO` with a callback that handles [parsing issues](SrcinfoParseIssue).
204    pub fn parse_with_issues<HandleIssue, Error>(
205        text: &'a str,
206        mut handle_issue: HandleIssue,
207    ) -> PartialParseResult<ParsedSrcinfo<'a>, Error>
208    where
209        HandleIssue: FnMut(SrcinfoParseIssue<'a>) -> Result<(), Error>,
210    {
211        let mut parsed = ParsedSrcinfo::default();
212        let lines = non_blank_trimmed_lines(text);
213        let mut section_mut = parsed.get_or_insert(Section::Base);
214
215        macro_rules! return_or_continue {
216            ($issue:expr) => {
217                match handle_issue($issue) {
218                    Err(error) => return PartialParseResult::new_partial(parsed, error),
219                    Ok(()) => continue,
220                }
221            };
222        }
223
224        for line in lines {
225            let Some((field, value)) = parse_line(line) else {
226                return_or_continue!(SrcinfoParseIssue::InvalidLine(line));
227            };
228            let Ok(field) = field.to_parsed::<FieldName, &str>() else {
229                return_or_continue!(SrcinfoParseIssue::UnknownField(field));
230            };
231            if value.is_empty() {
232                continue;
233            }
234            match section_mut.add(field, value) {
235                Ok(()) => {}
236                Err(AddFailure::MeetHeader(name)) => {
237                    section_mut.shrink_to_fit();
238                    section_mut = parsed.get_or_insert(Section::Derivative(name));
239                }
240                Err(AddFailure::Issue(issue)) => {
241                    return_or_continue!(issue);
242                }
243            }
244        }
245
246        PartialParseResult::new_complete(parsed)
247    }
248}
249
250impl<'a> PartialParse<&'a str> for ParsedSrcinfo<'a> {
251    type Error = SrcinfoParseError<'a>;
252    fn partial_parse(input: &'a str) -> PartialParseResult<Self, Self::Error> {
253        ParsedSrcinfo::parse(input)
254    }
255}
256
257impl<'a, HandleIssue, Error> ParseWithIssues<&'a str, HandleIssue, Error> for ParsedSrcinfo<'a>
258where
259    HandleIssue: FnMut(SrcinfoParseIssue<'a>) -> Result<(), Error>,
260{
261    fn parse_with_issues(
262        input: &'a str,
263        handle_issue: HandleIssue,
264    ) -> PartialParseResult<Self, Error> {
265        ParsedSrcinfo::parse_with_issues(input, handle_issue)
266    }
267}
268
269/// Try parsing a `.SRCINFO` text, [unknown fields are ignored](SrcinfoParseIssue::ignore_unknown_field), partial success means error.
270impl<'a> TryFrom<&'a str> for ParsedSrcinfo<'a> {
271    /// Error that occurs when parsing fails or incomplete.
272    type Error = SrcinfoParseError<'a>;
273    /// Try parsing a `.SRCINFO` text, [unknown fields are ignored](SrcinfoParseIssue::ignore_unknown_field), partial success means error.
274    fn try_from(text: &'a str) -> Result<Self, Self::Error> {
275        ParsedSrcinfo::parse(text).try_into_complete()
276    }
277}
278
279impl ReuseAdvice for ParsedSrcinfo<'_> {
280    /// [`ParsedSrcinfo`] costs O(n) time to construct (n being text length).
281    /// Performing a lookup on it costs O(1) time.
282    ///
283    /// This struct is designed to be reused.
284    type ShouldReuse = True;
285}