gix_config/file/mutable/
multi_value.rs

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