arch_pkg_text/desc/query/
memo.rs

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