1use crate::Section;
4use crate::wif::{ParseError, SequenceError};
5use indexmap::{IndexMap, indexmap};
6use std::cmp::Ordering;
7use std::collections::HashMap;
8use std::num::{ParseFloatError, ParseIntError};
9use std::{slice, vec};
10use strum::EnumString;
11
12const TRUES: [&str; 6] = ["true", "yes", "t", "y", "on", "1"];
13const FALSES: [&str; 6] = ["false", "no", "f", "n", "off", "0"];
14
15pub trait WifValue {
17 const EXPECTED_TYPE: &'static str;
19 fn present(&self) -> bool;
21 fn parse(string_value: &str, key_for_err: &str) -> Result<Self, ParseError>
26 where
27 Self: Sized;
28
29 #[must_use]
31 fn type_error(string_value: &str, key_for_err: &str) -> ParseError {
32 ParseError::BadValueType {
33 value: string_value.to_owned(),
34 key: key_for_err.to_owned(),
35 expected_type: Self::EXPECTED_TYPE.to_owned(),
36 }
37 }
38
39 fn parse_arr(string_value: &str, key_for_err: &str) -> Result<Vec<usize>, ParseError> {
44 string_value
45 .split(',')
46 .map(|s| s.trim().parse::<usize>())
47 .collect::<Result<Vec<usize>, ParseIntError>>()
48 .map_err(|_| Self::type_error(string_value, key_for_err))
49 }
50
51 fn to_wif_string(&self) -> String;
53}
54
55#[derive(Clone, PartialEq, Eq, Debug, Copy)]
58pub struct WifColor(pub usize, pub usize, pub usize);
59
60impl WifValue for WifColor {
61 const EXPECTED_TYPE: &'static str = "color triple";
62
63 fn present(&self) -> bool {
64 true
65 }
66
67 fn parse(string_value: &str, key_for_err: &str) -> Result<Self, ParseError> {
68 let values = Self::parse_arr(string_value, key_for_err)?;
69 match values.len() {
70 3 => Ok(Self(values[0], values[1], values[2])),
71 _ => Err(Self::type_error(string_value, key_for_err)),
72 }
73 }
74
75 fn to_wif_string(&self) -> String {
76 format!("{0},{1},{2}", self.0, self.1, self.2)
77 }
78}
79
80impl WifValue for usize {
81 const EXPECTED_TYPE: &'static str = "non-negative integer";
82
83 fn present(&self) -> bool {
84 *self > 0
85 }
86 fn parse(string_value: &str, key_for_err: &str) -> Result<Self, ParseError> {
87 string_value
88 .trim()
89 .parse::<Self>()
90 .map_err(|_| Self::type_error(string_value, key_for_err))
91 }
92
93 fn to_wif_string(&self) -> String {
94 self.to_string()
95 }
96}
97
98impl WifValue for bool {
99 const EXPECTED_TYPE: &'static str = "ini boolean";
100
101 fn present(&self) -> bool {
102 true
103 }
104
105 fn parse(string_value: &str, key_for_err: &str) -> Result<Self, ParseError>
106 where
107 Self: Sized,
108 {
109 let lower = string_value.trim().to_lowercase();
110 if TRUES.contains(&lower.as_str()) {
111 Ok(true)
112 } else if FALSES.contains(&lower.as_str()) {
113 Ok(false)
114 } else {
115 Err(Self::type_error(string_value, key_for_err))
116 }
117 }
118
119 fn to_wif_string(&self) -> String {
120 self.to_string()
121 }
122}
123
124impl WifValue for Vec<usize> {
125 const EXPECTED_TYPE: &'static str = "list of shafts";
126 fn present(&self) -> bool {
127 !self.is_empty()
128 }
129 fn parse(string_value: &str, key_for_err: &str) -> Result<Self, ParseError> {
130 Self::parse_arr(string_value, key_for_err)
131 }
132
133 fn to_wif_string(&self) -> String {
134 self.iter()
135 .map(ToString::to_string)
136 .collect::<Vec<String>>()
137 .join(",")
138 }
139}
140
141impl WifValue for (usize, usize) {
142 const EXPECTED_TYPE: &'static str = "Integer pair";
143
144 fn present(&self) -> bool {
145 true
146 }
147
148 fn parse(string_value: &str, key_for_err: &str) -> Result<Self, ParseError>
149 where
150 Self: Sized,
151 {
152 let vec = Self::parse_arr(string_value, key_for_err)?;
153 match vec.len() {
154 2 => Ok((vec[0], vec[1])),
155 _ => Err(Self::type_error(string_value, key_for_err)),
156 }
157 }
158
159 fn to_wif_string(&self) -> String {
160 format!("{}, {}", self.0, self.1)
161 }
162}
163
164#[derive(Debug, Clone, PartialOrd, PartialEq, Eq)]
168pub struct SequenceEntry<T>
169where
170 T: WifValue + Clone,
171{
172 index: usize,
173 value: T,
174}
175
176impl<T: WifValue + Clone> SequenceEntry<T> {
177 pub const fn index(&self) -> usize {
179 self.index
180 }
181
182 pub const fn value(&self) -> &T {
186 &self.value
187 }
188
189 pub fn value_option(&self) -> Option<&T> {
193 self.value.present().then_some(&self.value)
194 }
195}
196
197impl SequenceEntry<Vec<usize>> {
198 fn to_single(&self) -> Result<SequenceEntry<usize>, usize> {
199 let new_value = match self.value.len() {
200 0 => 0,
201 1 => self.value[0],
202 _ => return Err(self.index),
203 };
204 Ok(SequenceEntry {
205 index: self.index,
206 value: new_value,
207 })
208 }
209}
210
211pub trait WifParseable {
213 fn from_index_map(conf_data: &IndexMap<String, Option<String>>) -> (Self, Vec<ParseError>)
218 where
219 Self: Sized;
220
221 fn to_index_map(&self) -> IndexMap<String, Option<String>>;
223}
224
225#[derive(PartialEq, Eq, Debug, Clone)]
227pub struct ColorMetadata(ParsedValue<(usize, usize)>);
228
229impl ColorMetadata {
230 pub(crate) const fn missing() -> Self {
231 Self(ParsedValue(Err((
232 ParseError::MissingDependentSection {
233 missing_section: Section::ColorPalette,
234 dependent_section: Section::ColorTable,
235 },
236 None,
237 ))))
238 }
239
240 pub(crate) const fn inner(&self) -> &ParsedValue<(usize, usize)> {
241 &self.0
242 }
243
244 pub(crate) const fn as_option(&self) -> Option<&Self> {
245 match self.0.0 {
246 Err((ParseError::MissingDependentSection { .. }, ..)) => None,
247 _ => Some(self),
248 }
249 }
250}
251
252impl WifParseable for ColorMetadata {
253 fn from_index_map(conf_data: &IndexMap<String, Option<String>>) -> (Self, Vec<ParseError>) {
254 let parsed: ParsedValue<(usize, usize)> = ParsedValue::parse_required("range", conf_data);
255 let errors: Vec<ParseError> = parsed
256 .0
257 .as_ref()
258 .err()
259 .map_or(vec![], |e| vec![e.0.clone()]);
260
261 (Self(parsed), errors)
262 }
263
264 fn to_index_map(&self) -> IndexMap<String, Option<String>> {
265 indexmap! {
266 String::from("Range") => self.0.to_wif_string()
267 }
268 }
269}
270
271#[derive(PartialEq, Eq, Debug, Clone)]
276pub struct WifSequence<T: Clone + WifValue>(pub Vec<SequenceEntry<T>>);
277
278impl WifSequence<Vec<usize>> {
279 pub fn to_single_sequence(&self) -> Result<WifSequence<usize>, usize> {
284 Ok(WifSequence(
285 self.0
286 .iter()
287 .map(SequenceEntry::to_single)
288 .collect::<Result<Vec<SequenceEntry<usize>>, usize>>()?,
289 ))
290 }
291}
292
293#[derive(Debug)]
295pub struct SequenceIterDefault<'a, T: Clone + WifValue + Default> {
296 index: usize,
298 inner_index: usize,
300 sequence: &'a WifSequence<T>,
301}
302
303impl<T: Clone + WifValue + Default> Iterator for SequenceIterDefault<'_, T> {
304 type Item = T;
305
306 fn next(&mut self) -> Option<Self::Item> {
307 self.index += 1;
308 if self.inner_index >= self.sequence.0.len() {
309 return None;
310 }
311
312 if self.index > self.sequence.0[self.inner_index].index {
313 Some(Default::default())
314 } else {
315 self.inner_index += 1;
316 Some(self.sequence.0[self.inner_index - 1].value.clone())
317 }
318 }
319}
320
321#[derive(Debug)]
324pub struct SequenceIterOption<'a, T: Clone + WifValue> {
325 index: usize,
327 inner_index: usize,
329 sequence: &'a WifSequence<T>,
330}
331
332impl<'a, T: Clone + WifValue> Iterator for SequenceIterOption<'a, T> {
333 type Item = Option<&'a T>;
334
335 fn next(&mut self) -> Option<Self::Item> {
336 self.index += 1;
337 if self.inner_index >= self.sequence.0.len() {
338 return None;
339 }
340
341 if self.index > self.sequence.0[self.inner_index].index {
342 Some(None)
343 } else {
344 self.inner_index += 1;
345 Some(Some(&self.sequence.0[self.inner_index - 1].value))
346 }
347 }
348}
349
350impl<T: Clone + WifValue> IntoIterator for WifSequence<T> {
351 type Item = SequenceEntry<T>;
352 type IntoIter = vec::IntoIter<SequenceEntry<T>>;
353
354 fn into_iter(self) -> Self::IntoIter {
355 self.0.into_iter()
356 }
357}
358
359impl<T: Clone + WifValue> WifParseable for WifSequence<T> {
360 fn from_index_map(conf_data: &IndexMap<String, Option<String>>) -> (Self, Vec<ParseError>) {
362 let mut sequence = Vec::new();
363 let mut errors = Vec::new();
364 for (key, value) in conf_data {
365 let Some(value) = value.as_ref() else {
366 errors.push(ParseError::MissingValue(key.clone()));
367 continue;
368 };
369 let Ok(index) = key.parse::<usize>() else {
370 errors.push(ParseError::BadIntegerKey(key.clone()));
371 continue;
372 };
373
374 let result = T::parse(value, key);
375 match result {
376 Ok(value) => sequence.push(SequenceEntry { index, value }),
377 Err(e) => errors.push(e),
378 }
379 }
380 (Self(sequence), errors)
381 }
382
383 fn to_index_map(&self) -> IndexMap<String, Option<String>> {
384 let mut map = IndexMap::new();
385 self.0.iter().for_each(|e| {
386 map.insert(e.index.to_string(), Some(e.value.to_wif_string()));
387 });
388
389 map
390 }
391}
392
393impl<T: Copy + WifValue> WifSequence<T> {
394 #[must_use]
396 pub fn to_map(&self) -> HashMap<usize, T> {
397 self.entry_iter().map(|e| (e.index, e.value)).collect()
398 }
399}
400
401impl<T: Clone + WifValue + Default> WifSequence<T> {
402 pub fn from_option_array(sequence: &[Option<T>]) -> Self {
404 Self(
405 sequence
406 .iter()
407 .enumerate()
408 .map(|(index, value)| {
409 let value = value.as_ref();
410 SequenceEntry {
411 index: index + 1,
412 value: value.map_or_else(Default::default, Clone::clone),
413 }
414 })
415 .collect(),
416 )
417 }
418
419 #[must_use]
421 pub const fn default_iter(&self) -> SequenceIterDefault<T> {
422 SequenceIterDefault {
423 index: 0,
424 inner_index: 0,
425 sequence: self,
426 }
427 }
428}
429
430impl<T: Clone + WifValue> WifSequence<T> {
431 pub fn from_array(sequence: &[T]) -> Self {
433 Self(
434 sequence
435 .iter()
436 .enumerate()
437 .map(|(index, value)| SequenceEntry {
438 index: index + 1,
439 value: value.clone(),
440 })
441 .collect(),
442 )
443 }
444 #[must_use]
446 pub fn to_borrowed_map(&self) -> HashMap<usize, &T> {
447 self.entry_iter().map(|e| (e.index, &e.value)).collect()
448 }
449
450 pub fn entry_iter(&self) -> slice::Iter<SequenceEntry<T>> {
452 self.0.iter()
453 }
454
455 #[must_use]
457 pub const fn option_iter(&self) -> SequenceIterOption<T> {
458 SequenceIterOption {
459 index: 0,
460 inner_index: 0,
461 sequence: self,
462 }
463 }
464
465 pub fn validate(&self) -> Result<(), SequenceError> {
482 if !self.0.is_empty() && self.0[0].index == 0 {
483 return Err(SequenceError::Zero(0));
484 }
485
486 for i in 0..(self.0.len() - 1) {
487 let pair = &self.0[i..(i + 2)];
488 let ok_index = pair[0].index;
489 let maybe_index = pair[1].index;
490 match ok_index.cmp(&maybe_index) {
491 Ordering::Less => {}
492 Ordering::Equal => {
493 return Err(SequenceError::Repeat {
494 last_ok_position: i,
495 error_position: i + 1,
496 duplicate_index: ok_index,
497 });
498 }
499 Ordering::Greater => {
500 return Err(SequenceError::OutOfOrder {
501 last_ok_index: ok_index,
502 out_of_order_index: maybe_index,
503 out_of_order_position: i + 1,
504 });
505 }
506 }
507 }
508 Ok(())
509 }
510}
511
512pub type OptionalValue<T> = Option<ParsedValue<T>>;
514
515#[derive(Debug, Clone, PartialEq, Eq)]
517pub struct ParsedValue<T>(Result<T, (ParseError, Option<String>)>)
518where
519 T: WifValue;
520
521impl<T> ParsedValue<T>
522where
523 T: WifValue,
524{
525 fn parse(wif_string: &str, key: &str) -> Self {
526 let result = T::parse(wif_string, key);
527 Self(result.map_err(|e| (e, Some(wif_string.to_owned()))))
528 }
529
530 pub const fn as_result(&self) -> Result<&T, &(ParseError, Option<String>)> {
535 self.0.as_ref()
536 }
537
538 pub fn as_option(&self) -> Option<&T> {
540 self.0.as_ref().ok()
541 }
542
543 pub fn error(&self) -> Option<&ParseError> {
545 self.0.as_ref().map_err(|e| &e.0).err()
546 }
547
548 pub(crate) fn parse_optional(
549 key: &str,
550 map: &IndexMap<String, Option<String>>,
551 ) -> Option<Self> {
552 match map.get(&key.to_lowercase()) {
553 None => None,
554 Some(None) => Some(Self(Err((ParseError::MissingValue(key.to_owned()), None)))),
555 Some(Some(wif_string)) => Some(Self::parse(wif_string, key)),
556 }
557 }
558
559 pub(crate) fn parse_required(key: &str, map: &IndexMap<String, Option<String>>) -> Self {
560 let lower_key = key.to_lowercase();
561 match map.get(&lower_key) {
562 None => Self(Err((ParseError::MissingField(key.to_owned()), None))),
563 Some(None) => Self(Err((ParseError::MissingValue(key.to_owned()), None))),
564 Some(Some(wif_string)) => Self::parse(wif_string, key),
565 }
566 }
567
568 pub fn to_wif_string(&self) -> Option<String> {
570 match &self.0 {
571 Ok(v) => Some(v.to_wif_string()),
572 Err((_, wif_string)) => wif_string.clone(),
573 }
574 }
575
576 pub(crate) fn insert(&self, key: String, map: &mut IndexMap<String, Option<String>>) {
577 map.insert(key, self.to_wif_string());
578 }
579}
580
581#[derive(Clone, Debug, EnumString, PartialEq, Eq, Copy, strum::Display)]
583#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
584pub enum ThreadUnit {
585 Centimeters,
587 Inches,
589 Decipoints,
591}
592
593impl WifValue for ThreadUnit {
594 const EXPECTED_TYPE: &'static str = "inches, centimeters, or decipoints";
595
596 fn present(&self) -> bool {
597 true
598 }
599
600 fn parse(string_value: &str, key_for_err: &str) -> Result<Self, ParseError>
601 where
602 Self: Sized,
603 {
604 Self::try_from(string_value).map_err(|_| Self::type_error(string_value, key_for_err))
605 }
606
607 fn to_wif_string(&self) -> String {
608 self.to_string()
609 }
610}
611
612#[derive(Debug, Clone, Eq, PartialEq)]
617pub struct WifDecimal(String);
618
619#[expect(
620 clippy::fallible_impl_from,
621 reason = "WifDecimal guarantees the value is parseable as a float"
622)]
623impl From<WifDecimal> for f64 {
624 fn from(value: WifDecimal) -> Self {
625 value.0.parse().unwrap()
626 }
627}
628
629impl TryFrom<String> for WifDecimal {
630 type Error = ParseFloatError;
631
632 fn try_from(value: String) -> Result<Self, Self::Error> {
633 let _: f64 = value.parse()?;
634
635 Ok(Self(value))
636 }
637}
638
639impl WifValue for WifDecimal {
640 const EXPECTED_TYPE: &'static str = "decimal";
641
642 fn present(&self) -> bool {
643 true
644 }
645
646 fn parse(string_value: &str, key_for_err: &str) -> Result<Self, ParseError>
647 where
648 Self: Sized,
649 {
650 Self::try_from(string_value.to_owned())
651 .map_err(|_| Self::type_error(string_value, key_for_err))
652 }
653
654 fn to_wif_string(&self) -> String {
655 self.0.clone()
656 }
657}
658
659#[cfg(test)]
660mod tests {
661 use super::*;
662
663 #[test]
664 fn parse_vec() {
665 assert_eq!(Vec::parse("1,4,6,8", "").unwrap(), vec![1, 4, 6, 8]);
666 assert_eq!(Vec::parse("1 ,4 ,6, 8 ", "").unwrap(), vec![1, 4, 6, 8]);
667 assert_eq!(
668 Vec::parse("1,4,6,8,a", "").unwrap_err(),
669 ParseError::BadValueType {
670 key: String::new(),
671 value: String::from("1,4,6,8,a"),
672 expected_type: String::from("list of shafts")
673 }
674 );
675 assert_eq!(
676 Vec::parse("asdlf", "").unwrap_err(),
677 ParseError::BadValueType {
678 key: String::new(),
679 value: String::from("asdlf"),
680 expected_type: String::from("list of shafts")
681 }
682 );
683 assert_eq!(
684 Vec::parse("-1", "").unwrap_err(),
685 ParseError::BadValueType {
686 key: String::new(),
687 value: String::from("-1"),
688 expected_type: String::from("list of shafts")
689 }
690 );
691 }
692
693 #[test]
694 fn parse_color() {
695 assert_eq!(WifColor::parse("1,0,5", "").unwrap(), WifColor(1, 0, 5));
696 assert_eq!(
697 WifColor::parse("1,0,5,7", "").unwrap_err(),
698 ParseError::BadValueType {
699 value: String::from("1,0,5,7"),
700 key: String::new(),
701 expected_type: String::from("color triple")
702 }
703 );
704 assert_eq!(
705 WifColor::parse("1 ,0,5 ", "").unwrap(),
706 WifColor(1, 0, 5)
707 );
708 assert_eq!(
709 WifColor::parse("1,", "").unwrap_err(),
710 ParseError::BadValueType {
711 key: String::new(),
712 value: String::from("1,"),
713 expected_type: String::from("color triple")
714 }
715 );
716 }
717}