summaryrefslogtreecommitdiffstats
path: root/vendor/gix-config/src/file/mutable/mod.rs
blob: 506a5484d02f1a76a0d7a20a72a06d3a69102e8b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use std::borrow::Cow;

use bstr::{BStr, BString, ByteSlice, ByteVec};

use crate::{file, parse::Event};

pub(crate) mod multi_value;
pub(crate) mod section;
pub(crate) mod value;

fn escape_value(value: &BStr) -> BString {
    let starts_with_whitespace = value.first().map_or(false, u8::is_ascii_whitespace);
    let ends_with_whitespace = value
        .get(value.len().saturating_sub(1))
        .map_or(false, u8::is_ascii_whitespace);
    let contains_comment_indicators = value.find_byteset(b";#").is_some();
    let quote = starts_with_whitespace || ends_with_whitespace || contains_comment_indicators;

    let mut buf: BString = Vec::with_capacity(value.len()).into();
    if quote {
        buf.push(b'"');
    }

    for b in value.iter().copied() {
        match b {
            b'\n' => buf.push_str("\\n"),
            b'\t' => buf.push_str("\\t"),
            b'"' => buf.push_str("\\\""),
            b'\\' => buf.push_str("\\\\"),
            _ => buf.push(b),
        }
    }

    if quote {
        buf.push(b'"');
    }
    buf
}

#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
struct Whitespace<'a> {
    pre_key: Option<Cow<'a, BStr>>,
    pre_sep: Option<Cow<'a, BStr>>,
    post_sep: Option<Cow<'a, BStr>>,
}

impl Default for Whitespace<'_> {
    fn default() -> Self {
        Whitespace {
            pre_key: Some(b"\t".as_bstr().into()),
            pre_sep: Some(b" ".as_bstr().into()),
            post_sep: Some(b" ".as_bstr().into()),
        }
    }
}

impl<'a> Whitespace<'a> {
    fn key_value_separators(&self) -> Vec<Event<'a>> {
        let mut out = Vec::with_capacity(3);
        if let Some(ws) = &self.pre_sep {
            out.push(Event::Whitespace(ws.clone()));
        }
        out.push(Event::KeyValueSeparator);
        if let Some(ws) = &self.post_sep {
            out.push(Event::Whitespace(ws.clone()));
        }
        out
    }

    fn from_body(s: &file::section::Body<'a>) -> Self {
        let key_pos =
            s.0.iter()
                .enumerate()
                .find_map(|(idx, e)| matches!(e, Event::SectionKey(_)).then(|| idx));
        key_pos
            .map(|key_pos| {
                let pre_key = s.0[..key_pos].iter().next_back().and_then(|e| match e {
                    Event::Whitespace(s) => Some(s.clone()),
                    _ => None,
                });
                let from_key = &s.0[key_pos..];
                let (pre_sep, post_sep) = from_key
                    .iter()
                    .enumerate()
                    .find_map(|(idx, e)| matches!(e, Event::KeyValueSeparator).then(|| idx))
                    .map(|sep_pos| {
                        (
                            from_key.get(sep_pos - 1).and_then(|e| match e {
                                Event::Whitespace(ws) => Some(ws.clone()),
                                _ => None,
                            }),
                            from_key.get(sep_pos + 1).and_then(|e| match e {
                                Event::Whitespace(ws) => Some(ws.clone()),
                                _ => None,
                            }),
                        )
                    })
                    .unwrap_or_default();
                Whitespace {
                    pre_key,
                    pre_sep,
                    post_sep,
                }
            })
            .unwrap_or_default()
    }
}