gix_config/parse/section/
mod.rs1use std::{borrow::Cow, fmt::Display};
2
3use bstr::BStr;
4
5use crate::parse::{Event, Section};
6
7pub mod header;
9
10pub(crate) mod unvalidated;
11
12#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
14pub struct Header<'a> {
15 pub(crate) name: Name<'a>,
17 pub(crate) separator: Option<Cow<'a, BStr>>,
23 pub(crate) subsection_name: Option<Cow<'a, BStr>>,
24}
25
26impl Section<'_> {
27 #[must_use]
29 pub fn to_owned(&self) -> Section<'static> {
30 Section {
31 header: self.header.to_owned(),
32 events: self.events.iter().map(Event::to_owned).collect(),
33 }
34 }
35}
36
37impl Display for Section<'_> {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 write!(f, "{}", self.header)?;
40 for event in &self.events {
41 event.fmt(f)?;
42 }
43 Ok(())
44 }
45}
46
47mod types {
48 macro_rules! generate_case_insensitive {
49 ($name:ident, $module:ident, $err_doc:literal, $validate:ident, $cow_inner_type:ty, $comment:literal) => {
50 pub mod $module {
52 #[derive(Debug, thiserror::Error, Copy, Clone)]
54 #[error($err_doc)]
55 pub struct Error;
56 }
57
58 #[doc = $comment]
59 #[derive(Clone, Eq, Debug, Default)]
60 pub struct $name<'a>(pub(crate) std::borrow::Cow<'a, $cow_inner_type>);
61
62 impl<'a> $name<'a> {
63 pub(crate) fn from_str_unchecked(s: &'a str) -> Self {
64 $name(std::borrow::Cow::Borrowed(s.into()))
65 }
66 #[must_use]
68 pub fn to_owned(&self) -> $name<'static> {
69 $name(std::borrow::Cow::Owned(self.0.clone().into_owned()))
70 }
71 }
72
73 impl PartialEq for $name<'_> {
74 fn eq(&self, other: &Self) -> bool {
75 self.0.eq_ignore_ascii_case(&other.0)
76 }
77 }
78
79 impl std::fmt::Display for $name<'_> {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 self.0.fmt(f)
82 }
83 }
84
85 impl PartialOrd for $name<'_> {
86 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
87 Some(self.cmp(other))
88 }
89 }
90
91 impl Ord for $name<'_> {
92 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
93 let a = self.0.iter().map(|c| c.to_ascii_lowercase());
94 let b = other.0.iter().map(|c| c.to_ascii_lowercase());
95 a.cmp(b)
96 }
97 }
98
99 impl std::hash::Hash for $name<'_> {
100 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
101 for b in self.0.iter() {
102 b.to_ascii_lowercase().hash(state);
103 }
104 }
105 }
106
107 impl<'a> std::convert::TryFrom<&'a str> for $name<'a> {
108 type Error = $module::Error;
109
110 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
111 Self::try_from(std::borrow::Cow::Borrowed(bstr::ByteSlice::as_bstr(s.as_bytes())))
112 }
113 }
114
115 impl<'a> std::convert::TryFrom<String> for $name<'a> {
116 type Error = $module::Error;
117
118 fn try_from(s: String) -> Result<Self, Self::Error> {
119 Self::try_from(std::borrow::Cow::Owned(bstr::BString::from(s)))
120 }
121 }
122
123 impl<'a> std::convert::TryFrom<std::borrow::Cow<'a, bstr::BStr>> for $name<'a> {
124 type Error = $module::Error;
125
126 fn try_from(s: std::borrow::Cow<'a, bstr::BStr>) -> Result<Self, Self::Error> {
127 if $validate(s.as_ref()) {
128 Ok(Self(s))
129 } else {
130 Err($module::Error)
131 }
132 }
133 }
134
135 impl<'a> std::ops::Deref for $name<'a> {
136 type Target = $cow_inner_type;
137
138 fn deref(&self) -> &Self::Target {
139 &self.0
140 }
141 }
142
143 impl<'a> std::convert::AsRef<str> for $name<'a> {
144 fn as_ref(&self) -> &str {
145 std::str::from_utf8(self.0.as_ref()).expect("only valid UTF8 makes it through our validation")
146 }
147 }
148 };
149 }
150
151 fn is_valid_name(n: &bstr::BStr) -> bool {
152 !n.is_empty() && n.iter().all(|b| b.is_ascii_alphanumeric() || *b == b'-')
153 }
154 fn is_valid_value_name(n: &bstr::BStr) -> bool {
155 is_valid_name(n) && n[0].is_ascii_alphabetic()
156 }
157
158 generate_case_insensitive!(
159 Name,
160 name,
161 "Valid names consist of alphanumeric characters or dashes.",
162 is_valid_name,
163 bstr::BStr,
164 "Wrapper struct for section header names, like `remote`, since these are case-insensitive."
165 );
166
167 generate_case_insensitive!(
168 ValueName,
169 value_name,
170 "Valid value names consist of alphanumeric characters or dashes, starting with an alphabetic character.",
171 is_valid_value_name,
172 bstr::BStr,
173 "Wrapper struct for value names, like `path` in `include.path`, since keys are case-insensitive."
174 );
175}
176pub use types::{name, value_name, Name, ValueName};
177
178pub(crate) fn into_cow_bstr(c: Cow<'_, str>) -> Cow<'_, BStr> {
179 match c {
180 Cow::Borrowed(s) => Cow::Borrowed(s.into()),
181 Cow::Owned(s) => Cow::Owned(s.into()),
182 }
183}