Skip to main content

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