]>
Commit | Line | Data |
---|---|---|
0a29b90c FG |
1 | use std::{borrow::Cow, ops::Deref}; |
2 | ||
3 | use bstr::{BStr, BString, ByteSlice}; | |
4 | use smallvec::SmallVec; | |
5 | ||
6 | use crate::{ | |
7 | file, | |
8 | file::{Metadata, Section, SectionMut}, | |
9 | parse, | |
10 | parse::{section, Event}, | |
11 | }; | |
12 | ||
13 | pub(crate) mod body; | |
14 | pub use body::{Body, BodyIter}; | |
15 | use gix_features::threading::OwnShared; | |
16 | ||
17 | use crate::file::{ | |
18 | write::{extract_newline, platform_newline}, | |
19 | SectionId, | |
20 | }; | |
21 | ||
22 | impl<'a> Deref for Section<'a> { | |
23 | type Target = Body<'a>; | |
24 | ||
25 | fn deref(&self) -> &Self::Target { | |
26 | &self.body | |
27 | } | |
28 | } | |
29 | ||
30 | /// Instantiation and conversion | |
31 | impl<'a> Section<'a> { | |
32 | /// Create a new section with the given `name` and optional, `subsection`, `meta`-data and an empty body. | |
33 | pub fn new( | |
34 | name: impl Into<Cow<'a, str>>, | |
35 | subsection: impl Into<Option<Cow<'a, BStr>>>, | |
36 | meta: impl Into<OwnShared<file::Metadata>>, | |
37 | ) -> Result<Self, parse::section::header::Error> { | |
38 | Ok(Section { | |
39 | header: parse::section::Header::new(name, subsection)?, | |
40 | body: Default::default(), | |
41 | meta: meta.into(), | |
42 | id: SectionId::default(), | |
43 | }) | |
44 | } | |
45 | } | |
46 | ||
47 | /// Access | |
48 | impl<'a> Section<'a> { | |
49 | /// Return our header. | |
50 | pub fn header(&self) -> §ion::Header<'a> { | |
51 | &self.header | |
52 | } | |
53 | ||
54 | /// Return the unique `id` of the section, for use with the `*_by_id()` family of methods | |
55 | /// in [gix_config::File][crate::File]. | |
56 | pub fn id(&self) -> SectionId { | |
57 | self.id | |
58 | } | |
59 | ||
60 | /// Return our body, containing all keys and values. | |
61 | pub fn body(&self) -> &Body<'a> { | |
62 | &self.body | |
63 | } | |
64 | ||
65 | /// Serialize this type into a `BString` for convenience. | |
66 | /// | |
67 | /// Note that `to_string()` can also be used, but might not be lossless. | |
68 | #[must_use] | |
69 | pub fn to_bstring(&self) -> BString { | |
70 | let mut buf = Vec::new(); | |
71 | self.write_to(&mut buf).expect("io error impossible"); | |
72 | buf.into() | |
73 | } | |
74 | ||
75 | /// Stream ourselves to the given `out`, in order to reproduce this section mostly losslessly | |
76 | /// as it was parsed. | |
77 | pub fn write_to(&self, mut out: impl std::io::Write) -> std::io::Result<()> { | |
78 | self.header.write_to(&mut out)?; | |
79 | ||
80 | if self.body.0.is_empty() { | |
81 | return Ok(()); | |
82 | } | |
83 | ||
84 | let nl = self | |
85 | .body | |
86 | .as_ref() | |
87 | .iter() | |
88 | .find_map(extract_newline) | |
89 | .unwrap_or_else(|| platform_newline()); | |
90 | ||
91 | if !self | |
92 | .body | |
93 | .as_ref() | |
94 | .iter() | |
95 | .take_while(|e| !matches!(e, Event::SectionKey(_))) | |
96 | .any(|e| e.to_bstr_lossy().contains_str(nl)) | |
97 | { | |
98 | out.write_all(nl)?; | |
99 | } | |
100 | ||
101 | let mut saw_newline_after_value = true; | |
102 | let mut in_key_value_pair = false; | |
103 | for (idx, event) in self.body.as_ref().iter().enumerate() { | |
104 | match event { | |
105 | Event::SectionKey(_) => { | |
106 | if !saw_newline_after_value { | |
107 | out.write_all(nl)?; | |
108 | } | |
109 | saw_newline_after_value = false; | |
110 | in_key_value_pair = true; | |
111 | } | |
112 | Event::Newline(_) if !in_key_value_pair => { | |
113 | saw_newline_after_value = true; | |
114 | } | |
115 | Event::Value(_) | Event::ValueDone(_) => { | |
116 | in_key_value_pair = false; | |
117 | } | |
118 | _ => {} | |
119 | } | |
120 | event.write_to(&mut out)?; | |
121 | if let Event::ValueNotDone(_) = event { | |
122 | if self | |
123 | .body | |
124 | .0 | |
125 | .get(idx + 1) | |
126 | .filter(|e| matches!(e, Event::Newline(_))) | |
127 | .is_none() | |
128 | { | |
129 | out.write_all(nl)?; | |
130 | } | |
131 | } | |
132 | } | |
133 | Ok(()) | |
134 | } | |
135 | ||
136 | /// Return additional information about this sections origin. | |
137 | pub fn meta(&self) -> &Metadata { | |
138 | &self.meta | |
139 | } | |
140 | ||
141 | /// Returns a mutable version of this section for adjustment of values. | |
142 | pub fn to_mut(&mut self, newline: SmallVec<[u8; 2]>) -> SectionMut<'_, 'a> { | |
143 | SectionMut::new(self, newline) | |
144 | } | |
145 | } |