1use super::{tag_from_bytes, tag_from_str_lossy, Tag};
2use core::fmt;
3
4#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
6pub struct Setting<T> {
7 pub tag: Tag,
9 pub value: T,
11}
12
13impl<T: fmt::Display> fmt::Display for Setting<T> {
14 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
15 let bytes = self.tag.to_be_bytes();
16 let tag_name = core::str::from_utf8(&bytes).unwrap_or("");
17 write!(f, "\"{}\" {}", tag_name, self.value)
18 }
19}
20
21impl Setting<u16> {
22 pub fn parse(s: &str) -> Option<Self> {
24 Self::parse_list(s).next()
25 }
26
27 pub fn parse_list(s: &str) -> impl Iterator<Item = Self> + '_ + Clone {
30 ParseList::new(s)
31 .map(|(_, tag, value_str)| {
32 let (ok, value) = match value_str {
33 "on" | "" => (true, 1),
34 "off" => (true, 0),
35 _ => match value_str.parse::<u16>() {
36 Ok(value) => (true, value),
37 _ => (false, 0),
38 },
39 };
40 (ok, tag, value)
41 })
42 .take_while(|(ok, _, _)| *ok)
43 .map(|(_, tag, value)| Self { tag, value })
44 }
45}
46
47impl Setting<f32> {
48 pub fn parse(s: &str) -> Option<Self> {
50 Self::parse_list(s).next()
51 }
52
53 pub fn parse_list(s: &str) -> impl Iterator<Item = Self> + '_ + Clone {
56 ParseList::new(s)
57 .map(|(_, tag, value_str)| {
58 let (ok, value) = match value_str.parse::<f32>() {
59 Ok(value) => (true, value),
60 _ => (false, 0.),
61 };
62 (ok, tag, value)
63 })
64 .take_while(|(ok, _, _)| *ok)
65 .map(|(_, tag, value)| Self { tag, value })
66 }
67}
68
69impl<T> From<(Tag, T)> for Setting<T> {
70 fn from(v: (Tag, T)) -> Self {
71 Self {
72 tag: v.0,
73 value: v.1,
74 }
75 }
76}
77
78impl<T: Copy> From<&(Tag, T)> for Setting<T> {
79 fn from(v: &(Tag, T)) -> Self {
80 Self {
81 tag: v.0,
82 value: v.1,
83 }
84 }
85}
86
87impl<T: Copy> From<&([u8; 4], T)> for Setting<T> {
88 fn from(v: &([u8; 4], T)) -> Self {
89 Self {
90 tag: tag_from_bytes(&v.0),
91 value: v.1,
92 }
93 }
94}
95
96impl<T: Copy> From<&(&[u8; 4], T)> for Setting<T> {
97 fn from(v: &(&[u8; 4], T)) -> Self {
98 Self {
99 tag: tag_from_bytes(v.0),
100 value: v.1,
101 }
102 }
103}
104
105impl<T> From<(&str, T)> for Setting<T> {
106 fn from(v: (&str, T)) -> Self {
107 Self {
108 tag: tag_from_str_lossy(v.0),
109 value: v.1,
110 }
111 }
112}
113
114impl<T: Copy> From<&(&str, T)> for Setting<T> {
115 fn from(v: &(&str, T)) -> Self {
116 Self {
117 tag: tag_from_str_lossy(v.0),
118 value: v.1,
119 }
120 }
121}
122
123#[derive(Clone)]
124struct ParseList<'a> {
125 source: &'a [u8],
126 len: usize,
127 pos: usize,
128}
129
130impl<'a> ParseList<'a> {
131 fn new(source: &'a str) -> Self {
132 Self {
133 source: source.as_bytes(),
134 len: source.len(),
135 pos: 0,
136 }
137 }
138}
139
140impl<'a> Iterator for ParseList<'a> {
141 type Item = (usize, Tag, &'a str);
142
143 fn next(&mut self) -> Option<Self::Item> {
144 let mut pos = self.pos;
145 while pos < self.len && {
146 let ch = self.source[pos];
147 ch.is_ascii_whitespace() || ch == b','
148 } {
149 pos += 1;
150 }
151 self.pos = pos;
152 if pos >= self.len {
153 return None;
154 }
155 let first = self.source[pos];
156 let mut start = pos;
157 let quote = match first {
158 b'"' | b'\'' => {
159 pos += 1;
160 start += 1;
161 first
162 }
163 _ => return None,
164 };
165 let mut tag_str = None;
166 while pos < self.len {
167 if self.source[pos] == quote {
168 tag_str = core::str::from_utf8(self.source.get(start..pos)?).ok();
169 pos += 1;
170 break;
171 }
172 pos += 1;
173 }
174 self.pos = pos;
175 let tag_str = tag_str?;
176 if tag_str.len() != 4 || !tag_str.is_ascii() {
177 return None;
178 }
179 let tag = tag_from_str_lossy(tag_str);
180 while pos < self.len {
181 if !self.source[pos].is_ascii_whitespace() {
182 break;
183 }
184 pos += 1;
185 }
186 self.pos = pos;
187 start = pos;
188 let mut end = start;
189 while pos < self.len {
190 if self.source[pos] == b',' {
191 pos += 1;
192 break;
193 }
194 pos += 1;
195 end += 1;
196 }
197 let value = core::str::from_utf8(self.source.get(start..end)?)
198 .ok()?
199 .trim();
200 self.pos = pos;
201 Some((pos, tag, value))
202 }
203}