arch_pkg_text/desc/query/
memo.rs1use super::QueryMut;
2use crate::{
3 desc::{
4 field::{FieldName, ParsedField, RawField},
5 misc::{ReuseAdvice, True},
6 },
7 parse::{ParseWithIssues, PartialParse, PartialParseResult},
8};
9use core::convert::Infallible;
10use pipe_trait::Pipe;
11
12#[derive(Debug, Clone)]
14pub struct MemoQuerier<'a> {
15 text: &'a str,
16 cache: Cache<'a>,
17 last: Option<(&'a str, RawField<'a>)>,
18}
19
20impl<'a> MemoQuerier<'a> {
21 pub fn new(text: &'a str) -> Self {
23 MemoQuerier {
24 text,
25 cache: Cache::default(),
26 last: None,
27 }
28 }
29
30 fn next_entry(&mut self) -> Option<(RawField<'a>, &'a str)> {
32 let mut lines = self.text.lines();
33
34 let (field_str, raw_field) = if let Some((field_str, raw_field)) = self.last {
35 lines.next()?;
36 (field_str, raw_field)
37 } else {
38 let field_str = lines.next()?.trim();
39 let raw_field = RawField::parse_raw(field_str).ok()?;
40 (field_str, raw_field)
41 };
42
43 let value_start_offset =
44 field_str.as_ptr() as usize + field_str.len() - self.text.as_ptr() as usize;
45 let next = lines.find_map(|line| -> Option<(&'a str, RawField<'a>)> {
46 let field_str = line.trim();
47 let raw_field = RawField::parse_raw(field_str).ok()?;
48 Some((field_str, raw_field))
49 });
50
51 let Some((next_field_str, next_raw_field)) = next else {
52 let value = self.text[value_start_offset..].trim_matches(['\n', '\r']);
53 self.text = "";
54 self.last = None;
55 return Some((raw_field, value));
56 };
57
58 let value_end_offset = next_field_str.as_ptr() as usize - self.text.as_ptr() as usize;
59 let value = self.text[value_start_offset..value_end_offset].trim_matches(['\n', '\r']);
60
61 self.last = Some((next_field_str, next_raw_field));
63 self.text = &self.text[value_end_offset..];
64
65 Some((raw_field, value))
66 }
67
68 #[doc(hidden)]
70 pub fn __has_cache(&self, field: FieldName) -> bool {
71 self.cache.get(&field).is_some()
72 }
73}
74
75impl<'a> QueryMut<'a> for MemoQuerier<'a> {
76 fn query_raw_text_mut(&mut self, field: ParsedField) -> Option<&'a str> {
77 if let Some(value) = self.cache.get(field.name()) {
78 return value;
79 }
80
81 while let Some((raw_field, value)) = self.next_entry() {
82 let Ok(parsed_field) = raw_field.to_parsed::<FieldName>() else {
83 continue;
84 };
85 let value = if value.is_empty() { None } else { Some(value) };
86 self.cache.add(&parsed_field, value);
87 if parsed_field == field {
88 return value;
89 }
90 }
91
92 None
93 }
94}
95
96macro_rules! def_cache {
97 ($(
98 $(#[$attrs:meta])*
99 $field:ident $(,)? $(;)?
100 )*) => {
101 #[derive(Debug, Clone, Copy)]
102 enum CacheErr {
103 OccupiedWithNone,
104 Unoccupied,
105 }
106
107 #[derive(Debug, Clone)]
108 #[allow(non_snake_case, reason = "We don't access the field names directly, keep it simple.")]
109 struct Cache<'a> {$(
110 $(#[$attrs])*
111 $field: Result<&'a str, CacheErr>, )*}
113
114 impl<'a> Cache<'a> {
115 fn get(&self, field: &FieldName) -> Option<Option<&'a str>> {
116 match field {$(
117 FieldName::$field => match self.$field {
118 Ok(value) => Some(Some(value)),
119 Err(CacheErr::OccupiedWithNone) => Some(None),
120 Err(CacheErr::Unoccupied) => None,
121 },
122 )*}
123 }
124
125 fn add(&mut self, field: &FieldName, value: Option<&'a str>) {
126 match (field, value) {$(
127 (FieldName::$field, Some(value)) => self.$field = Ok(value),
128 (FieldName::$field, None) => self.$field = Err(CacheErr::OccupiedWithNone),
129 )*}
130 }
131 }
132
133 impl<'a> Default for Cache<'a> {
134 fn default() -> Self {
135 Cache {$(
136 $field: Err(CacheErr::Unoccupied),
137 )*}
138 }
139 }
140
141 #[test]
142 fn test_cache_fields() {$({
143 use pretty_assertions::assert_eq;
144 let field = &FieldName::$field;
145 let mut cache = Cache::default();
146 assert_eq!(cache.get(field), None);
147 cache.add(field, None);
148 assert_eq!(cache.get(field), Some(None));
149 cache.add(field, Some("foo"));
150 assert_eq!(cache.get(field), Some(Some("foo")));
151 })*}
152 };
153}
154
155def_cache!(
156 FileName Name Base Version Description Groups
157 CompressedSize InstalledSize Md5Checksum Sha256Checksum
158 PgpSignature Url License Architecture BuildDate Packager
159 Dependencies CheckDependencies MakeDependencies OptionalDependencies
160 Provides Conflicts Replaces
161);
162
163impl ReuseAdvice for MemoQuerier<'_> {
164 type ShouldReuse = True;
169}
170
171impl<'a> From<&'a str> for MemoQuerier<'a> {
172 fn from(value: &'a str) -> Self {
173 MemoQuerier::new(value)
174 }
175}
176
177impl<'a> PartialParse<&'a str> for MemoQuerier<'a> {
178 type Error = Infallible;
179 fn partial_parse(text: &'a str) -> PartialParseResult<Self, Self::Error> {
180 MemoQuerier::parse_with_issues(text, ())
181 }
182}
183
184impl<'a, HandleIssue, Error> ParseWithIssues<&'a str, HandleIssue, Error> for MemoQuerier<'a> {
185 fn parse_with_issues(text: &'a str, _: HandleIssue) -> PartialParseResult<Self, Error> {
186 text.pipe(MemoQuerier::new)
187 .pipe(PartialParseResult::new_complete)
188 }
189}