mod checksums;
mod data;
use super::PartialParseResult;
use crate::{
srcinfo::{
utils::{non_blank_trimmed_lines, parse_line},
Field, FieldName, ParsedField, RawField, Section,
},
value,
};
use derive_more::{Display, Error};
use indexmap::IndexMap;
use pipe_trait::Pipe;
pub use data::{
ParsedSrcinfoBaseSection, ParsedSrcinfoBaseUniqueFieldDuplicationError,
ParsedSrcinfoDerivativeSection, ParsedSrcinfoDerivativeUniqueFieldDuplicationError,
};
#[derive(Debug, Default, Clone)]
pub struct ParsedSrcinfo<'a> {
pub base: ParsedSrcinfoBaseSection<'a>,
pub derivatives: IndexMap<value::Name<'a>, ParsedSrcinfoDerivativeSection<'a>>,
}
enum ParsedSrcinfoSectionMut<'a, 'r> {
Base(&'r mut ParsedSrcinfoBaseSection<'a>),
Derivative(ParsedSrcinfoDerivativeSectionEntryMut<'a, 'r>),
}
impl<'a> ParsedSrcinfo<'a> {
fn get_or_insert(&mut self, section: Section<'a>) -> ParsedSrcinfoSectionMut<'a, '_> {
match section {
Section::Base => self.base.pipe_mut(ParsedSrcinfoSectionMut::Base),
Section::Derivative(name) => self
.derivatives
.entry(name)
.or_default()
.pipe(|data| ParsedSrcinfoDerivativeSectionEntryMut::new(name, data))
.pipe(ParsedSrcinfoSectionMut::Derivative),
}
}
}
enum AddFailure<'a> {
MeetHeader(value::Name<'a>),
Issue(SrcinfoParseIssue<'a>),
}
fn unknown_field_from_parsed(field: ParsedField<&str>) -> Result<(), AddFailure<'_>> {
Field::blank()
.with_name(field.name_str())
.with_architecture(field.architecture_str())
.pipe(SrcinfoParseIssue::UnknownField)
.pipe(AddFailure::Issue)
.pipe(Err)
}
impl<'a> ParsedSrcinfoSectionMut<'a, '_> {
fn add(&mut self, field: ParsedField<&'a str>, value: &'a str) -> Result<(), AddFailure<'a>> {
match self {
ParsedSrcinfoSectionMut::Base(section) => section.add(field, value),
ParsedSrcinfoSectionMut::Derivative(section) => section.add(field, value),
}
}
fn shrink_to_fit(&mut self) {
match self {
ParsedSrcinfoSectionMut::Base(section) => section.shrink_to_fit(),
ParsedSrcinfoSectionMut::Derivative(section) => section.shrink_to_fit(),
}
}
}
impl<'a> ParsedSrcinfoBaseSection<'a> {
fn add_value_to_option<Value: Copy>(
target: &mut Option<Value>,
value: &'a str,
make_value: impl FnOnce(&'a str) -> Value,
make_error: impl FnOnce(Value) -> ParsedSrcinfoBaseUniqueFieldDuplicationError<'a>,
) -> Result<(), AddFailure<'a>> {
let Some(old_value) = target else {
*target = Some(make_value(value));
return Ok(());
};
(*old_value)
.pipe(make_error)
.pipe(SrcinfoParseIssue::BaseUniqueFieldDuplication)
.pipe(AddFailure::Issue)
.pipe(Err)
}
}
struct ParsedSrcinfoDerivativeSectionEntryMut<'a, 'r> {
name: value::Name<'a>,
data: &'r mut ParsedSrcinfoDerivativeSection<'a>,
}
impl<'a, 'r> ParsedSrcinfoDerivativeSectionEntryMut<'a, 'r> {
fn new(name: value::Name<'a>, data: &'r mut ParsedSrcinfoDerivativeSection<'a>) -> Self {
ParsedSrcinfoDerivativeSectionEntryMut { name, data }
}
fn add_value_to_option<Value: Copy>(
name: value::Name<'a>,
target: &mut Option<Value>,
value: &'a str,
make_value: impl FnOnce(&'a str) -> Value,
make_error: impl FnOnce(Value) -> ParsedSrcinfoDerivativeUniqueFieldDuplicationError<'a>,
) -> Result<(), AddFailure<'a>> {
let Some(old_value) = target else {
*target = Some(make_value(value));
return Ok(());
};
(*old_value)
.pipe(make_error)
.pipe(move |error| SrcinfoParseIssue::DerivativeUniqueFieldDuplication(name, error))
.pipe(AddFailure::Issue)
.pipe(Err)
}
fn shrink_to_fit(&mut self) {
self.data.shrink_to_fit()
}
}
#[derive(Debug, Display, Error, Clone, Copy)]
pub enum SrcinfoParseError<'a> {
#[display("Failed to insert value to the pkgbase section: {_0}")]
BaseUniqueFieldDuplication(
#[error(not(source))] ParsedSrcinfoBaseUniqueFieldDuplicationError<'a>,
),
#[display("Failed to insert value to the pkgname section named {_0}: {_1}")]
DerivativeUniqueFieldDuplication(
value::Name<'a>,
ParsedSrcinfoDerivativeUniqueFieldDuplicationError<'a>,
),
#[display("Invalid line: {_0:?}")]
InvalidLine(#[error(not(source))] &'a str),
}
pub type SrcinfoParseReturn<'a> = PartialParseResult<ParsedSrcinfo<'a>, SrcinfoParseError<'a>>;
#[derive(Debug, Clone, Copy)]
pub enum SrcinfoParseIssue<'a> {
UnknownField(RawField<'a>),
BaseUniqueFieldDuplication(ParsedSrcinfoBaseUniqueFieldDuplicationError<'a>),
DerivativeUniqueFieldDuplication(
value::Name<'a>,
ParsedSrcinfoDerivativeUniqueFieldDuplicationError<'a>,
),
InvalidLine(&'a str),
}
impl<'a> SrcinfoParseIssue<'a> {
fn ignore_unknown_field(self) -> Result<(), SrcinfoParseError<'a>> {
Err(match self {
SrcinfoParseIssue::UnknownField(_) => return Ok(()),
SrcinfoParseIssue::BaseUniqueFieldDuplication(error) => {
SrcinfoParseError::BaseUniqueFieldDuplication(error)
}
SrcinfoParseIssue::DerivativeUniqueFieldDuplication(name, error) => {
SrcinfoParseError::DerivativeUniqueFieldDuplication(name, error)
}
SrcinfoParseIssue::InvalidLine(line) => SrcinfoParseError::InvalidLine(line),
})
}
}
impl<'a> ParsedSrcinfo<'a> {
pub fn parse(text: &'a str) -> SrcinfoParseReturn<'a> {
ParsedSrcinfo::parse_with_issues(text, SrcinfoParseIssue::ignore_unknown_field)
}
pub fn parse_with_issues<HandleIssue, Error>(
text: &'a str,
mut handle_issue: HandleIssue,
) -> PartialParseResult<ParsedSrcinfo<'a>, Error>
where
HandleIssue: FnMut(SrcinfoParseIssue<'a>) -> Result<(), Error>,
{
let mut parsed = ParsedSrcinfo::default();
let lines = non_blank_trimmed_lines(text);
let mut section_mut = parsed.get_or_insert(Section::Base);
macro_rules! return_or_continue {
($issue:expr) => {
match handle_issue($issue) {
Err(error) => return PartialParseResult::new_partial(parsed, error),
Ok(()) => continue,
}
};
}
for line in lines {
let Some((field, value)) = parse_line(line) else {
return_or_continue!(SrcinfoParseIssue::InvalidLine(line));
};
let Ok(field) = field.to_parsed::<FieldName, &str>() else {
return_or_continue!(SrcinfoParseIssue::UnknownField(field));
};
if value.is_empty() {
continue;
}
match section_mut.add(field, value) {
Ok(()) => {}
Err(AddFailure::MeetHeader(name)) => {
section_mut.shrink_to_fit();
section_mut = parsed.get_or_insert(Section::Derivative(name));
}
Err(AddFailure::Issue(issue)) => {
return_or_continue!(issue);
}
}
}
PartialParseResult::new_complete(parsed)
}
}
impl<'a> TryFrom<&'a str> for ParsedSrcinfo<'a> {
type Error = SrcinfoParseError<'a>;
fn try_from(text: &'a str) -> Result<Self, Self::Error> {
ParsedSrcinfo::parse(text).try_into_complete()
}
}