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