Skip to main content

gix_config/file/mutable/
multi_value.rs

1use std::{collections::HashMap, ops::DerefMut};
2
3use bstr::{BStr, BString, ByteVec};
4
5use crate::{
6    file::{
7        self, SectionData, SectionId,
8        mutable::{Whitespace, escape_value},
9    },
10    lookup,
11    parse::{Event, section},
12    value::normalize,
13};
14
15/// Internal data structure for [`MutableMultiValue`]
16#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
17pub(crate) struct EntryData {
18    pub(crate) section_id: SectionId,
19    pub(crate) offset_index: usize,
20}
21
22/// An intermediate representation of a mutable multivar obtained from a [`File`][crate::File].
23#[derive(Debug)]
24pub struct MultiValueMut<'borrow> {
25    pub(crate) section: &'borrow mut HashMap<SectionId, SectionData>,
26    pub(crate) backing: &'borrow mut Vec<u8>,
27    pub(crate) key: section::ValueName,
28    /// Each entry data struct provides sufficient information to index into
29    /// [`Self::offsets`]. This layer of indirection is used for users to index
30    /// into the offsets rather than leaking the internal data structures.
31    pub(crate) indices_and_sizes: Vec<EntryData>,
32    /// Each offset represents the size of a event slice and whether or not the
33    /// event slice is significant or not. This is used to index into the
34    /// actual section.
35    pub(crate) offsets: HashMap<SectionId, Vec<usize>>,
36}
37
38impl MultiValueMut<'_> {
39    /// Returns the actual values.
40    pub fn get(&self) -> Result<Vec<BString>, lookup::existing::Error> {
41        let mut expect_value = false;
42        let mut values = Vec::new();
43        let mut concatenated_value = BString::default();
44
45        for EntryData {
46            section_id,
47            offset_index,
48        } in &self.indices_and_sizes
49        {
50            let (offset, size) = MultiValueMut::index_and_size(&self.offsets, *section_id, *offset_index);
51            for event in &self.section.get(section_id).expect("known section id").as_ref()[offset..offset + size] {
52                match event {
53                    Event::SectionValueName(section_key)
54                        if section_key
55                            .as_bstr_in(self.backing)
56                            .eq_ignore_ascii_case(self.key.0.as_slice()) =>
57                    {
58                        expect_value = true;
59                    }
60                    Event::Value(v) if expect_value => {
61                        expect_value = false;
62                        values.push(normalize(v.as_slice_in(self.backing)).into_owned());
63                    }
64                    Event::ValueNotDone(v) if expect_value => concatenated_value.push_str(v.as_slice_in(self.backing)),
65                    Event::ValueDone(v) if expect_value => {
66                        expect_value = false;
67                        concatenated_value.push_str(v.as_slice_in(self.backing));
68                        let concatenated_value = std::mem::take(&mut concatenated_value);
69                        values.push(normalize(&concatenated_value).into_owned());
70                    }
71                    _ => (),
72                }
73            }
74        }
75
76        if values.is_empty() {
77            return Err(lookup::existing::Error::KeyMissing);
78        }
79
80        Ok(values)
81    }
82
83    /// Returns the amount of values within this multivar.
84    #[must_use]
85    pub fn len(&self) -> usize {
86        self.indices_and_sizes.len()
87    }
88
89    /// Returns true if the multivar does not have any values.
90    /// This might occur if the value was deleted but wasn't yet set with a new value.
91    #[must_use]
92    pub fn is_empty(&self) -> bool {
93        self.indices_and_sizes.is_empty()
94    }
95
96    /// Sets the value at the given index.
97    ///
98    /// # Safety
99    ///
100    /// This will panic if the index is out of range.
101    pub fn set_string_at(&mut self, index: usize, value: impl AsRef<str>) -> Result<(), crate::parse::span::Error> {
102        self.set_at(index, value.as_ref())
103    }
104
105    /// Sets the value at the given index.
106    ///
107    /// # Safety
108    ///
109    /// This will panic if the index is out of range.
110    pub fn set_at(&mut self, index: usize, value: impl crate::AsBStr) -> Result<(), crate::parse::span::Error> {
111        let EntryData {
112            section_id,
113            offset_index,
114        } = self.indices_and_sizes[index];
115        MultiValueMut::set_value_inner(
116            &self.key,
117            &mut self.offsets,
118            &mut self.section.get_mut(&section_id).expect("known section id").body,
119            self.backing,
120            section_id,
121            offset_index,
122            value.as_bstr(),
123        )
124    }
125
126    /// Sets all values to the provided ones. Note that this follows [`zip`]
127    /// logic: if the number of values in the input is less than the number of
128    /// values currently existing, then only the first `n` values are modified.
129    /// If more values are provided than there currently are, then the
130    /// remaining values are ignored.
131    ///
132    /// [`zip`]: std::iter::Iterator::zip
133    pub fn set_values<Iter, Item>(&mut self, values: Iter) -> Result<(), crate::parse::span::Error>
134    where
135        Iter: IntoIterator<Item = Item>,
136        Item: crate::AsBStr,
137    {
138        for (
139            EntryData {
140                section_id,
141                offset_index,
142            },
143            value,
144        ) in self.indices_and_sizes.iter().zip(values)
145        {
146            Self::set_value_inner(
147                &self.key,
148                &mut self.offsets,
149                &mut self.section.get_mut(section_id).expect("known section id").body,
150                self.backing,
151                *section_id,
152                *offset_index,
153                value.as_bstr(),
154            )?;
155        }
156        Ok(())
157    }
158
159    /// Sets all values in this multivar to the provided one without owning the
160    /// provided input.
161    pub fn set_all(&mut self, input: impl crate::AsBStr) -> Result<(), crate::parse::span::Error> {
162        let input = input.as_bstr();
163        for EntryData {
164            section_id,
165            offset_index,
166        } in &self.indices_and_sizes
167        {
168            Self::set_value_inner(
169                &self.key,
170                &mut self.offsets,
171                &mut self.section.get_mut(section_id).expect("known section id").body,
172                self.backing,
173                *section_id,
174                *offset_index,
175                input,
176            )?;
177        }
178        Ok(())
179    }
180
181    fn set_value_inner(
182        value_name: &section::ValueName,
183        offsets: &mut HashMap<SectionId, Vec<usize>>,
184        section: &mut file::section::BodyData,
185        backing: &mut Vec<u8>,
186        section_id: SectionId,
187        offset_index: usize,
188        value: &BStr,
189    ) -> Result<(), crate::parse::span::Error> {
190        let (offset, size) = MultiValueMut::index_and_size(offsets, section_id, offset_index);
191        let whitespace = Whitespace::from_body(section, backing);
192        let value = crate::parse::Span::append(backing, &escape_value(value))?;
193        let key_sep_events = whitespace.key_value_separators(backing)?;
194        let key = crate::parse::Span::append(backing, value_name.0.as_slice())?;
195
196        let section = section.as_mut();
197        section.drain(offset..offset + size);
198        MultiValueMut::set_offset(offsets, section_id, offset_index, 2 + key_sep_events.len());
199        section.insert(offset, Event::Value(value));
200        section
201            .splice(offset..offset, key_sep_events.into_iter().rev())
202            .for_each(|_| {});
203        section.insert(offset, Event::SectionValueName(key));
204        Ok(())
205    }
206
207    /// Removes the value at the given index. Does nothing when called multiple
208    /// times in succession.
209    ///
210    /// # Safety
211    ///
212    /// This will panic if the index is out of range.
213    pub fn delete(&mut self, index: usize) {
214        let EntryData {
215            section_id,
216            offset_index,
217        } = &self.indices_and_sizes[index];
218        let (offset, size) = MultiValueMut::index_and_size(&self.offsets, *section_id, *offset_index);
219        if size == 0 {
220            return;
221        }
222        self.section
223            .get_mut(section_id)
224            .expect("known section id")
225            .body
226            .as_mut()
227            .drain(offset..offset + size);
228
229        Self::set_offset(&mut self.offsets, *section_id, *offset_index, 0);
230        self.indices_and_sizes.remove(index);
231    }
232
233    /// Removes all values. Does nothing when called multiple times in
234    /// succession.
235    pub fn delete_all(&mut self) {
236        for EntryData {
237            section_id,
238            offset_index,
239        } in &self.indices_and_sizes
240        {
241            let (offset, size) = MultiValueMut::index_and_size(&self.offsets, *section_id, *offset_index);
242            if size == 0 {
243                continue;
244            }
245            self.section
246                .get_mut(section_id)
247                .expect("known section id")
248                .body
249                .as_mut()
250                .drain(offset..offset + size);
251            Self::set_offset(&mut self.offsets, *section_id, *offset_index, 0);
252        }
253        self.indices_and_sizes.clear();
254    }
255
256    fn index_and_size(
257        offsets: &HashMap<SectionId, Vec<usize>>,
258        section_id: SectionId,
259        offset_index: usize,
260    ) -> (usize, usize) {
261        offsets
262            .get(&section_id)
263            .expect("known section id")
264            .iter()
265            .take(offset_index + 1)
266            .fold((0, 0), |(total_ofs, ofs), size| (total_ofs + ofs, *size))
267    }
268
269    // This must be an associated function rather than a method to allow Rust
270    // to split mutable borrows.
271    fn set_offset(
272        offsets: &mut HashMap<SectionId, Vec<usize>>,
273        section_id: SectionId,
274        offset_index: usize,
275        value: usize,
276    ) {
277        *offsets
278            .get_mut(&section_id)
279            .expect("known section id")
280            .get_mut(offset_index)
281            .unwrap()
282            .deref_mut() = value;
283    }
284}