arch_pkg_text/srcinfo/query/
memo.rs

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