arch_pkg_text/parse/
srcinfo.rs

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