arch_pkg_db/multi/
insert.rs

1use super::{MultiQueryDatabase, WithVersion};
2use crate::{
3    misc::{Attached, AttachedUtils, IntoAttached},
4    value::RepositoryName,
5};
6use arch_pkg_text::{
7    desc::{Query, QueryMut, misc::ShouldReuse},
8    value::{Name, ParseVersionError, ParsedVersion, Version},
9};
10use core::mem::replace;
11use derive_more::{Display, Error};
12use pipe_trait::Pipe;
13
14/// Error type of [`MultiQueryDatabase::insert`] and [`MultiQueryDatabase::insert_mut`].
15#[derive(Debug, Display, Clone, Error)]
16pub enum InsertError<'a> {
17    #[display("Querier does not provide a name")]
18    NoName,
19    #[display("Querier does not provide a version")]
20    NoVersion,
21    #[display("Version provided by the querier has invalid syntax: {_0}")]
22    ParseVersion(#[error(not(source))] ParseVersionError<'a>),
23}
24
25impl<'a, Querier: ShouldReuse> MultiQueryDatabase<'a, Querier> {
26    /// Add a querier of a `desc` file to the database.
27    ///
28    /// If an older querier already occupied the same pair of [name] and [repository], it will be returned inside `Ok(Some(_))`.
29    ///
30    /// [name]: arch_pkg_text::value::Name
31    /// [repository]: RepositoryName
32    fn insert_with<GetName, GetVersion>(
33        &mut self,
34        repository: RepositoryName<'a>,
35        mut querier: Querier,
36        get_name: GetName,
37        get_version: GetVersion,
38    ) -> Result<Option<WithVersion<'a, Querier>>, InsertError<'a>>
39    where
40        GetName: FnOnce(&mut Querier) -> Option<Name<'a>>,
41        GetVersion: FnOnce(&mut Querier) -> Option<Version<'a>>,
42    {
43        let name = get_name(&mut querier).ok_or(InsertError::NoName)?;
44        let version = querier
45            .pipe_mut(get_version)
46            .ok_or(InsertError::NoVersion)?
47            .parse()
48            .map_err(InsertError::ParseVersion)?;
49        self.internal
50            .entry(&name)
51            .or_default()
52            .internal
53            .insert(&repository, querier.into_attached(version))
54            .pipe(Ok)
55    }
56
57    /// Add an [immutable querier](Query) of a `desc` file to the database.
58    ///
59    /// If an older querier already occupied the same pair of [name] and [repository], it will be returned inside `Ok(Some(_))`.
60    ///
61    /// [name]: arch_pkg_text::value::Name
62    /// [repository]: RepositoryName
63    pub fn insert(
64        &mut self,
65        repository: RepositoryName<'a>,
66        querier: Querier,
67    ) -> Result<Option<WithVersion<'a, Querier>>, InsertError<'a>>
68    where
69        Querier: Query<'a>,
70    {
71        self.insert_with(
72            repository,
73            querier,
74            |querier| querier.name(),
75            |querier| querier.version(),
76        )
77    }
78
79    /// Add a [mutable querier](QueryMut) of a `desc` file to the database.
80    ///
81    /// If an older querier already occupied the same pair of [name] and [repository], it will be returned inside `Ok(Some(_))`.
82    ///
83    /// [name]: arch_pkg_text::value::Name
84    /// [repository]: RepositoryName
85    pub fn insert_mut(
86        &mut self,
87        repository: RepositoryName<'a>,
88        querier: Querier,
89    ) -> Result<Option<WithVersion<'a, Querier>>, InsertError<'a>>
90    where
91        Querier: QueryMut<'a>,
92    {
93        self.insert_with(repository, querier, Querier::name_mut, Querier::version_mut)
94    }
95}
96
97/// Return type of [`MultiQueryDatabase::insert_newer`] and [`MultiQueryDatabase::insert_newer_mut`] upon success.
98#[derive(Debug, Clone, Copy)]
99pub enum InsertNewerReturn<'a, Querier> {
100    /// The entry was unoccupied, the querier was successfully inserted.
101    Unoccupied,
102    /// The entry was occupied by a querier whose package version is older than the provided querier.
103    /// The provided querier thus replaced the old one.
104    Replaced(Attached<Querier, ParsedVersion<'a>>),
105    /// The entry was occupied by a querier whose package version is not older than the provided querier.
106    /// The occupied querier was kept, the provided querier was rejected.
107    Rejected(Attached<Querier, ParsedVersion<'a>>),
108}
109
110impl<'a, Querier: ShouldReuse> MultiQueryDatabase<'a, Querier> {
111    /// Add a querier of a `desc` file to the database unless the entry was already occupied by a querier whose
112    /// package version is not older than the provided querier.
113    fn insert_newer_with<GetName, GetVersion>(
114        &mut self,
115        repository: RepositoryName<'a>,
116        mut querier: Querier,
117        get_name: GetName,
118        get_version: GetVersion,
119    ) -> Result<InsertNewerReturn<'a, Querier>, InsertError<'a>>
120    where
121        GetName: FnOnce(&mut Querier) -> Option<Name<'a>>,
122        GetVersion: FnOnce(&mut Querier) -> Option<Version<'a>>,
123    {
124        let name = get_name(&mut querier).ok_or(InsertError::NoName)?;
125        let inserted_version = querier
126            .pipe_mut(get_version)
127            .ok_or(InsertError::NoVersion)?
128            .parse()
129            .map_err(InsertError::ParseVersion)?;
130
131        let inserted = querier.into_attached(inserted_version);
132
133        let multi_querier = self.internal.entry(&name).or_default();
134
135        let Some(existing) = multi_querier.internal.get_mut(repository.as_str()) else {
136            multi_querier.internal.insert(&repository, inserted);
137            return Ok(InsertNewerReturn::Unoccupied);
138        };
139
140        let existing_version = existing.attachment();
141        Ok(if existing_version < &inserted_version {
142            InsertNewerReturn::Replaced(replace(existing, inserted))
143        } else {
144            InsertNewerReturn::Rejected(inserted)
145        })
146    }
147
148    /// Add an [immutable querier](Query) of a `desc` file to the database unless the entry was already occupied by a querier whose
149    /// package version is not older than the provided querier.
150    pub fn insert_newer(
151        &mut self,
152        repository: RepositoryName<'a>,
153        querier: Querier,
154    ) -> Result<InsertNewerReturn<'a, Querier>, InsertError<'a>>
155    where
156        Querier: Query<'a>,
157    {
158        self.insert_newer_with(
159            repository,
160            querier,
161            |querier| querier.name(),
162            |querier| querier.version(),
163        )
164    }
165
166    /// Add a [mutable querier](QueryMut) of a `desc` file to the database unless the entry was already occupied by a querier whose
167    /// package version is not older than the provided querier.
168    pub fn insert_newer_mut(
169        &mut self,
170        repository: RepositoryName<'a>,
171        querier: Querier,
172    ) -> Result<InsertNewerReturn<'a, Querier>, InsertError<'a>>
173    where
174        Querier: QueryMut<'a>,
175    {
176        self.insert_newer_with(repository, querier, Querier::name_mut, Querier::version_mut)
177    }
178}