summaryrefslogtreecommitdiffstats
path: root/vendor/gix-config/src/file/section/mod.rs
blob: e8e3310849109ceda91d28cee2d2985264eead9f (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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use std::{borrow::Cow, ops::Deref};

use bstr::{BStr, BString, ByteSlice};
use smallvec::SmallVec;

use crate::{
    file,
    file::{Metadata, Section, SectionMut},
    parse,
    parse::{section, Event},
};

pub(crate) mod body;
pub use body::{Body, BodyIter};
use gix_features::threading::OwnShared;

use crate::file::{
    write::{extract_newline, platform_newline},
    SectionId,
};

impl<'a> Deref for Section<'a> {
    type Target = Body<'a>;

    fn deref(&self) -> &Self::Target {
        &self.body
    }
}

/// Instantiation and conversion
impl<'a> Section<'a> {
    /// Create a new section with the given `name` and optional, `subsection`, `meta`-data and an empty body.
    pub fn new(
        name: impl Into<Cow<'a, str>>,
        subsection: impl Into<Option<Cow<'a, BStr>>>,
        meta: impl Into<OwnShared<file::Metadata>>,
    ) -> Result<Self, parse::section::header::Error> {
        Ok(Section {
            header: parse::section::Header::new(name, subsection)?,
            body: Default::default(),
            meta: meta.into(),
            id: SectionId::default(),
        })
    }
}

/// Access
impl<'a> Section<'a> {
    /// Return our header.
    pub fn header(&self) -> &section::Header<'a> {
        &self.header
    }

    /// Return the unique `id` of the section, for use with the `*_by_id()` family of methods
    /// in [gix_config::File][crate::File].
    pub fn id(&self) -> SectionId {
        self.id
    }

    /// Return our body, containing all keys and values.
    pub fn body(&self) -> &Body<'a> {
        &self.body
    }

    /// Serialize this type into a `BString` for convenience.
    ///
    /// Note that `to_string()` can also be used, but might not be lossless.
    #[must_use]
    pub fn to_bstring(&self) -> BString {
        let mut buf = Vec::new();
        self.write_to(&mut buf).expect("io error impossible");
        buf.into()
    }

    /// Stream ourselves to the given `out`, in order to reproduce this section mostly losslessly
    /// as it was parsed.
    pub fn write_to(&self, mut out: impl std::io::Write) -> std::io::Result<()> {
        self.header.write_to(&mut out)?;

        if self.body.0.is_empty() {
            return Ok(());
        }

        let nl = self
            .body
            .as_ref()
            .iter()
            .find_map(extract_newline)
            .unwrap_or_else(|| platform_newline());

        if !self
            .body
            .as_ref()
            .iter()
            .take_while(|e| !matches!(e, Event::SectionKey(_)))
            .any(|e| e.to_bstr_lossy().contains_str(nl))
        {
            out.write_all(nl)?;
        }

        let mut saw_newline_after_value = true;
        let mut in_key_value_pair = false;
        for (idx, event) in self.body.as_ref().iter().enumerate() {
            match event {
                Event::SectionKey(_) => {
                    if !saw_newline_after_value {
                        out.write_all(nl)?;
                    }
                    saw_newline_after_value = false;
                    in_key_value_pair = true;
                }
                Event::Newline(_) if !in_key_value_pair => {
                    saw_newline_after_value = true;
                }
                Event::Value(_) | Event::ValueDone(_) => {
                    in_key_value_pair = false;
                }
                _ => {}
            }
            event.write_to(&mut out)?;
            if let Event::ValueNotDone(_) = event {
                if self
                    .body
                    .0
                    .get(idx + 1)
                    .filter(|e| matches!(e, Event::Newline(_)))
                    .is_none()
                {
                    out.write_all(nl)?;
                }
            }
        }
        Ok(())
    }

    /// Return additional information about this sections origin.
    pub fn meta(&self) -> &Metadata {
        &self.meta
    }

    /// Returns a mutable version of this section for adjustment of values.
    pub fn to_mut(&mut self, newline: SmallVec<[u8; 2]>) -> SectionMut<'_, 'a> {
        SectionMut::new(self, newline)
    }
}