arch_pkg_db/text/multi/
parse.rs

1use super::MultiTextCollection;
2use crate::{MultiQueryDatabase, Text, multi::InsertError, value::RepositoryName};
3use arch_pkg_text::desc::{Query, QueryMut, misc::ShouldReuse};
4use core::error::Error;
5use derive_more::Display;
6use pipe_trait::Pipe;
7use rayon::prelude::*;
8
9/// Error type when trying to create a [`MultiQueryDatabase`] from a [`MultiTextCollection`].
10#[derive(Debug, Display, Clone)]
11#[display(bound(ParseError: Display))]
12pub enum MultiTextCollectionParseError<'a, ParseError> {
13    Parse(ParseError),
14    Insert(InsertError<'a>),
15}
16
17// We implement Error manually because derive_more::Error was unable to handle it.
18// Issue: <https://github.com/JelteF/derive_more/issues/511>
19impl<'a, ParseError: Error> Error for MultiTextCollectionParseError<'a, ParseError> {
20    fn source(&self) -> Option<&(dyn Error + 'static)> {
21        match self {
22            MultiTextCollectionParseError::Parse(error) => error.source(),
23            MultiTextCollectionParseError::Insert(error) => error.source(),
24        }
25    }
26}
27
28/// Return type of [`MultiTextCollection::parse`] and [`MultiTextCollection::parse_mut`].
29type ParseResult<'a, Querier> = Result<
30    MultiQueryDatabase<'a, Querier>,
31    MultiTextCollectionParseError<'a, <&'a str as TryInto<Querier>>::Error>,
32>;
33
34impl<'a> MultiTextCollection<'a> {
35    /// Parse a database of queriers.
36    fn parse_with<Querier, Insert, InsertSuccess>(
37        &'a self,
38        mut insert: Insert,
39    ) -> ParseResult<'a, Querier>
40    where
41        &'a str: TryInto<Querier>,
42        Insert: FnMut(
43            &mut MultiQueryDatabase<'a, Querier>,
44            RepositoryName<'a>,
45            Querier,
46        ) -> Result<InsertSuccess, InsertError<'a>>,
47    {
48        let iter = self.iter();
49        let (lower_cap, _) = iter.size_hint();
50        let mut db = MultiQueryDatabase::with_capacity(lower_cap);
51
52        for (repository, text) in iter {
53            let querier = text
54                .as_str()
55                .try_into()
56                .map_err(MultiTextCollectionParseError::Parse)?;
57            insert(&mut db, repository, querier).map_err(MultiTextCollectionParseError::Insert)?;
58        }
59
60        Ok(db)
61    }
62
63    /// Parse a database of [immutable queriers](Query).
64    pub fn parse<Querier>(&'a self) -> ParseResult<'a, Querier>
65    where
66        &'a str: TryInto<Querier>,
67        Querier: Query<'a> + ShouldReuse,
68    {
69        self.parse_with(MultiQueryDatabase::insert)
70    }
71
72    /// Parse a database of [mutable queriers](QueryMut).
73    pub fn parse_mut<Querier>(&'a self) -> ParseResult<'a, Querier>
74    where
75        &'a str: TryInto<Querier>,
76        Querier: QueryMut<'a> + ShouldReuse,
77    {
78        self.parse_with(MultiQueryDatabase::insert_mut)
79    }
80
81    /// Parse a database of queriers in parallel.
82    fn par_parse_with<Querier, QueriersIntoDb>(
83        &'a self,
84        queriers_into_db: QueriersIntoDb,
85    ) -> ParseResult<'a, Querier>
86    where
87        &'a str: TryInto<Querier, Error: Send>,
88        Querier: Send,
89        QueriersIntoDb: FnOnce(
90            Vec<(RepositoryName<'a>, Querier)>,
91        ) -> Result<MultiQueryDatabase<'a, Querier>, InsertError>,
92    {
93        self.internal
94            .par_iter()
95            .flat_map(|(repository, collection)| {
96                collection
97                    .iter()
98                    .par_bridge() // TODO: maybe implement ParallelIterator?
99                    .map(Text::as_str)
100                    .map(TryInto::<Querier>::try_into)
101                    .map(move |querier| querier.map(|querier| (*repository, querier)))
102            })
103            .collect::<Result<Vec<_>, _>>()
104            .map_err(MultiTextCollectionParseError::Parse)?
105            .pipe(queriers_into_db)
106            .map_err(MultiTextCollectionParseError::Insert)
107    }
108
109    /// Parse a database of [immutable queriers](Query) in parallel.
110    pub fn par_parse<Querier>(&'a self) -> ParseResult<'a, Querier>
111    where
112        &'a str: TryInto<Querier, Error: Send>,
113        Querier: Query<'a> + ShouldReuse + Send,
114    {
115        self.par_parse_with(MultiQueryDatabase::from_queriers)
116    }
117
118    /// Parse a database of [mutable queriers](QueryMut) in parallel.
119    pub fn par_parse_mut<Querier>(&'a self) -> ParseResult<'a, Querier>
120    where
121        &'a str: TryInto<Querier, Error: Send>,
122        Querier: QueryMut<'a> + ShouldReuse + Send,
123    {
124        self.par_parse_with(MultiQueryDatabase::from_queriers_mut)
125    }
126}