arch_pkg_text/srcinfo/query/
memo.rs

1mod cache;
2
3use super::{
4    utils::{parse_line, trimmed_line_is_blank},
5    ChecksumType, ChecksumValue, ChecksumsMut, EncourageReuse, QueryChecksumItem, QueryMut,
6    QueryRawTextItem, Section,
7};
8use crate::{
9    srcinfo::field::FieldName,
10    value::{Architecture, Name},
11};
12use cache::Cache;
13use core::str::Lines;
14use pipe_trait::Pipe;
15
16/// [Query](QueryMut) with a cache.
17#[derive(Debug, Clone)]
18pub struct MemoQuerier<'a> {
19    remaining_lines: Lines<'a>,
20    current_section: Section<'a>,
21    cache: Cache<'a>,
22}
23
24impl<'a> MemoQuerier<'a> {
25    /// Query the fields of a `.SRCINFO` file with a cache.
26    pub fn new(srcinfo: &'a str) -> Self {
27        MemoQuerier {
28            remaining_lines: srcinfo.lines(),
29            current_section: Section::Base,
30            cache: Cache::default(),
31        }
32    }
33
34    /// Shrink the cache's capacity to fit its length.
35    pub fn shrink_cache_to_fit(&mut self) {
36        self.cache.shrink_to_fit();
37    }
38
39    /// Private function for testing the internal cache.
40    #[doc(hidden)]
41    pub fn __has_cache(&self, field_name: FieldName, index: usize) -> bool {
42        self.cache.get(field_name, index).is_some()
43    }
44
45    /// Parse the next key-value pair, save it the cache and return it.
46    fn next_entry(&mut self) -> Option<(FieldName, QueryRawTextItem<'a>)> {
47        let line = self.remaining_lines.next()?.trim();
48        if trimmed_line_is_blank(line) {
49            return self.next_entry();
50        }
51        let (raw_field, value) = parse_line(line)?;
52        let Ok(field) = raw_field.to_parsed::<FieldName, &str>() else {
53            return self.next_entry();
54        };
55        if value.is_empty() {
56            return self.next_entry();
57        }
58        let architecture = field.architecture_str().map(Architecture);
59        if *field.name() == FieldName::Name && architecture.is_none() {
60            self.current_section = value.pipe(Name).pipe(Section::Derivative);
61        }
62        let item = QueryRawTextItem::from_tuple3((value, self.current_section, architecture));
63        self.cache.add(*field.name(), item);
64        Some((*field.name(), item))
65    }
66}
67
68/// Return type of [`QueryMut::query_raw_text_mut`] on an instance of [`MemoQuerier`].
69struct QueryIter<'a, 'r> {
70    querier: &'r mut MemoQuerier<'a>,
71    field_name: FieldName,
72    index: usize,
73}
74
75impl<'a, 'r> QueryIter<'a, 'r> {
76    /// Create an iterator that queries `field_name` from `querier`.
77    fn new(querier: &'r mut MemoQuerier<'a>, field_name: FieldName) -> Self {
78        QueryIter {
79            querier,
80            field_name,
81            index: 0,
82        }
83    }
84}
85
86impl<'a> Iterator for QueryIter<'a, '_> {
87    type Item = QueryRawTextItem<'a>;
88    fn next(&mut self) -> Option<Self::Item> {
89        let QueryIter {
90            querier,
91            field_name,
92            index,
93        } = self;
94        loop {
95            if let Some(item) = querier.cache.get(*field_name, *index) {
96                *index += 1;
97                return Some(item);
98            } else {
99                querier.next_entry()?;
100                continue;
101            }
102        }
103    }
104}
105
106impl<'a> QueryMut<'a> for MemoQuerier<'a> {
107    fn query_raw_text_mut(
108        &mut self,
109        field_name: FieldName,
110    ) -> impl Iterator<Item = QueryRawTextItem<'a>> {
111        QueryIter::new(self, field_name)
112    }
113}
114
115/// Return type of [`ChecksumsMut::checksums_mut`] on an instance of [`MemoQuerier`].
116struct ChecksumIter<'a, 'r> {
117    querier: &'r mut MemoQuerier<'a>,
118    checksum_type_id: usize,
119    checksum_index: usize,
120}
121
122impl<'a, 'r> ChecksumIter<'a, 'r> {
123    /// Create an iterator that queries all checksums from `querier`.
124    fn new(querier: &'r mut MemoQuerier<'a>) -> Self {
125        ChecksumIter {
126            querier,
127            checksum_type_id: 0,
128            checksum_index: 0,
129        }
130    }
131}
132
133impl<'a> Iterator for ChecksumIter<'a, '_> {
134    type Item = QueryChecksumItem<'a>;
135    fn next(&mut self) -> Option<Self::Item> {
136        let ChecksumIter {
137            querier,
138            checksum_type_id,
139            checksum_index,
140        } = self;
141        loop {
142            let checksum_type = *ChecksumType::TYPES.get(*checksum_type_id)?;
143            let field_name = checksum_type.into_field_name();
144            if let Some(item) = querier.cache.get(field_name, *checksum_index) {
145                *checksum_index += 1;
146                return item
147                    .map(move |value| ChecksumValue::new(checksum_type, value))
148                    .pipe(Some);
149            } else if querier.next_entry().is_none() {
150                *checksum_type_id += 1;
151                *checksum_index = 0;
152            }
153        }
154    }
155}
156
157impl<'a> ChecksumsMut<'a> for MemoQuerier<'a> {
158    fn checksums_mut(&mut self) -> impl Iterator<Item = QueryChecksumItem<'a>> {
159        ChecksumIter::new(self)
160    }
161}
162
163impl EncourageReuse for MemoQuerier<'_> {
164    /// [`MemoQuerier`] costs O(1) time to construct. Performing a lookup on it
165    /// costs O(n) the first time and O(1) after that.
166    ///
167    /// This struct is designed to be reused.
168    const ENCOURAGE_REUSE: bool = true;
169}