279 lines · 9.7 KB · Rust
RawDownload
use std::{    borrow::Cow,    ops::{Deref, Range},}; use bstr::{BStr, BString, ByteSlice, ByteVec};use smallvec::SmallVec; use crate::{    file::{        self,        mutable::{escape_value, Whitespace},        Index, Section, Size,    },    lookup, parse,    parse::{section::Key, Event},    value::{normalize, normalize_bstr, normalize_bstring},}; /// A opaque type that represents a mutable reference to a section.#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]pub struct SectionMut<'a, 'event> {    section: &'a mut Section<'event>,    implicit_newline: bool,    whitespace: Whitespace<'event>,    newline: SmallVec<[u8; 2]>,} /// Mutating methods.impl<'a, 'event> SectionMut<'a, 'event> {    /// Adds an entry to the end of this section name `key` and `value`. If `value` is None`, no equal sign will be written leaving    /// just the key. This is useful for boolean values which are true if merely the key exists.    pub fn push<'b>(&mut self, key: Key<'event>, value: Option<&'b BStr>) {        let body = &mut self.section.body.0;        if let Some(ws) = &self.whitespace.pre_key {            body.push(Event::Whitespace(ws.clone()));        }         body.push(Event::SectionKey(key));        match value {            Some(value) => {                body.extend(self.whitespace.key_value_separators());                body.push(Event::Value(escape_value(value).into()));            }            None => body.push(Event::Value(Cow::Borrowed("".into()))),        }        if self.implicit_newline {            body.push(Event::Newline(BString::from(self.newline.to_vec()).into()));        }    }     /// Removes all events until a key value pair is removed. This will also    /// remove the whitespace preceding the key value pair, if any is found.    pub fn pop(&mut self) -> Option<(Key<'_>, Cow<'event, BStr>)> {        let mut values = Vec::new();        // events are popped in reverse order        let body = &mut self.section.body.0;        while let Some(e) = body.pop() {            match e {                Event::SectionKey(k) => {                    // pop leading whitespace                    if let Some(Event::Whitespace(_)) = body.last() {                        body.pop();                    }                     if values.len() == 1 {                        let value = values.pop().expect("vec is non-empty but popped to empty value");                        return Some((k, normalize(value)));                    }                     return Some((                        k,                        normalize_bstring({                            let mut s = BString::default();                            for value in values.into_iter().rev() {                                s.push_str(value.as_ref());                            }                            s                        }),                    ));                }                Event::Value(v) | Event::ValueNotDone(v) | Event::ValueDone(v) => values.push(v),                _ => (),            }        }        None    }     /// Sets the last key value pair if it exists, or adds the new value.    /// Returns the previous value if it replaced a value, or None if it adds    /// the value.    pub fn set<'b>(&mut self, key: Key<'event>, value: impl Into<&'b BStr>) -> Option<Cow<'event, BStr>> {        match self.key_and_value_range_by(&key) {            None => {                self.push(key, Some(value.into()));                None            }            Some((key_range, value_range)) => {                let value_range = value_range.unwrap_or(key_range.end - 1..key_range.end);                let range_start = value_range.start;                let ret = self.remove_internal(value_range, false);                self.section                    .body                    .0                    .insert(range_start, Event::Value(escape_value(value.into()).into()));                Some(ret)            }        }    }     /// Removes the latest value by key and returns it, if it exists.    pub fn remove(&mut self, key: impl AsRef<str>) -> Option<Cow<'event, BStr>> {        let key = Key::from_str_unchecked(key.as_ref());        let (key_range, _value_range) = self.key_and_value_range_by(&key)?;        Some(self.remove_internal(key_range, true))    }     /// Adds a new line event. Note that you don't need to call this unless    /// you've disabled implicit newlines.    pub fn push_newline(&mut self) {        self.section            .body            .0            .push(Event::Newline(Cow::Owned(BString::from(self.newline.to_vec()))));    }     /// Return the newline used when calling [`push_newline()`][Self::push_newline()].    pub fn newline(&self) -> &BStr {        self.newline.as_slice().as_bstr()    }     /// Enables or disables automatically adding newline events after adding    /// a value. This is _enabled by default_.    pub fn set_implicit_newline(&mut self, on: bool) {        self.implicit_newline = on;    }     /// Sets the exact whitespace to use before each newly created key-value pair,    /// with only whitespace characters being permissible.    ///    /// The default is 2 tabs.    /// Set to `None` to disable adding whitespace before a key value.    ///    /// # Panics    ///    /// If non-whitespace characters are used. This makes the method only suitable for validated    /// or known input.    pub fn set_leading_whitespace(&mut self, whitespace: Option<Cow<'event, BStr>>) {        assert!(            whitespace                .as_deref()                .map_or(true, |ws| ws.iter().all(|b| b.is_ascii_whitespace())),            "input whitespace must only contain whitespace characters."        );        self.whitespace.pre_key = whitespace;    }     /// Returns the whitespace this section will insert before the    /// beginning of a key, if any.    #[must_use]    pub fn leading_whitespace(&self) -> Option<&BStr> {        self.whitespace.pre_key.as_deref()    }     /// Returns the whitespace to be used before and after the `=` between the key    /// and the value.    ///    /// For example, `k = v` will have `(Some(" "), Some(" "))`, whereas `k=\tv` will    /// have `(None, Some("\t"))`.    #[must_use]    pub fn separator_whitespace(&self) -> (Option<&BStr>, Option<&BStr>) {        (self.whitespace.pre_sep.as_deref(), self.whitespace.post_sep.as_deref())    }} // Internal methods that may require exact indices for faster operations.impl<'a, 'event> SectionMut<'a, 'event> {    pub(crate) fn new(section: &'a mut Section<'event>, newline: SmallVec<[u8; 2]>) -> Self {        let whitespace = Whitespace::from_body(&section.body);        Self {            section,            implicit_newline: true,            whitespace,            newline,        }    }     pub(crate) fn get<'key>(        &self,        key: &Key<'key>,        start: Index,        end: Index,    ) -> Result<Cow<'_, BStr>, lookup::existing::Error> {        let mut expect_value = false;        let mut concatenated_value = BString::default();         for event in &self.section.0[start.0..end.0] {            match event {                Event::SectionKey(event_key) if event_key == key => expect_value = true,                Event::Value(v) if expect_value => return Ok(normalize_bstr(v.as_ref())),                Event::ValueNotDone(v) if expect_value => {                    concatenated_value.push_str(v.as_ref());                }                Event::ValueDone(v) if expect_value => {                    concatenated_value.push_str(v.as_ref());                    return Ok(normalize_bstring(concatenated_value));                }                _ => (),            }        }         Err(lookup::existing::Error::KeyMissing)    }     pub(crate) fn delete(&mut self, start: Index, end: Index) {        self.section.body.0.drain(start.0..end.0);    }     pub(crate) fn set_internal(&mut self, index: Index, key: Key<'event>, value: &BStr) -> Size {        let mut size = 0;         let body = &mut self.section.body.0;        body.insert(index.0, Event::Value(escape_value(value).into()));        size += 1;         let sep_events = self.whitespace.key_value_separators();        size += sep_events.len();        body.insert_many(index.0, sep_events.into_iter().rev());         body.insert(index.0, Event::SectionKey(key));        size += 1;         Size(size)    }     /// Performs the removal, assuming the range is valid.    fn remove_internal(&mut self, range: Range<usize>, fix_whitespace: bool) -> Cow<'event, BStr> {        let events = &mut self.section.body.0;        if fix_whitespace            && events                .get(range.end)                .map_or(false, |ev| matches!(ev, Event::Newline(_)))        {            events.remove(range.end);        }        let value = events            .drain(range.clone())            .fold(Cow::Owned(BString::default()), |mut acc: Cow<'_, BStr>, e| {                if let Event::Value(v) | Event::ValueNotDone(v) | Event::ValueDone(v) = e {                    acc.to_mut().extend(&**v);                }                acc            });        if fix_whitespace            && range                .start                .checked_sub(1)                .and_then(|pos| events.get(pos))                .map_or(false, |ev| matches!(ev, Event::Whitespace(_)))        {            events.remove(range.start - 1);        }        value    }} impl<'event> Deref for SectionMut<'_, 'event> {    type Target = file::Section<'event>;     fn deref(&self) -> &Self::Target {        self.section    }} impl<'event> file::section::Body<'event> {    pub(crate) fn as_mut(&mut self) -> &mut parse::section::Events<'event> {        &mut self.0    }}