Skip to main content

wif_weave/wif/
data.rs

1//! Module for handling the data types within a wif file
2
3use 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
15/// Trait for values in `.wif` that are parseable from a string
16pub trait WifValue {
17    /// Expected type to find in the wif file
18    const EXPECTED_TYPE: &'static str;
19    /// Whether this value should be treated as there or not
20    fn present(&self) -> bool;
21    /// Parse from the string value in the `.wif`.
22    ///
23    /// # Errors
24    /// When the value can't be parsed into the expected type
25    fn parse(string_value: &str, key_for_err: &str) -> Result<Self, ParseError>
26    where
27        Self: Sized;
28
29    /// Construct a parse error
30    #[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    /// Parse an array from the value
40    ///
41    /// # Errors
42    /// If the values aren't a comma separated list of positive integers
43    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    /// Serialize into a string
52    fn to_wif_string(&self) -> String;
53}
54
55/// An RGB tuple representing a thread color. Note that the color range is not always 0-255.
56/// The actual range is specified in [`ColorPalette`]
57#[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/// # Represents a single threading or treadling entry
165///
166/// A value of `0` represents no thread/treadle.
167#[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    /// Index of entry
178    pub const fn index(&self) -> usize {
179        self.index
180    }
181
182    /// Returns the value of the entry.
183    ///
184    /// 0 indicates no entry at this index. To get [`None`] use [`value_option`][Self::value_option]
185    pub const fn value(&self) -> &T {
186        &self.value
187    }
188
189    /// Returns value of the entry as an [Option]
190    ///
191    /// Similar to [`value`][Self::value] but returns [`None`] instead of `0`
192    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
211/// For data types that can be extracted from a wif
212pub trait WifParseable {
213    /// Parse the data from a section of the wif file
214    ///
215    /// # Errors
216    /// If the keys or values aren't the expected types
217    fn from_index_map(conf_data: &IndexMap<String, Option<String>>) -> (Self, Vec<ParseError>)
218    where
219        Self: Sized;
220
221    /// Serialize into an index map
222    fn to_index_map(&self) -> IndexMap<String, Option<String>>;
223}
224
225/// The color metadata in the `COLOR PALETTE` section of a wif. We only care about the range for the rgb values.
226#[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/// # Represents the sequence of numbers that compose a threading or treadling
272///
273/// [`SequenceEntry`]'s in the vector should be in order by in order by index, with no duplicates or
274/// missing entries, but this is not guaranteed when constructed from a `.wif` file.
275#[derive(PartialEq, Eq, Debug, Clone)]
276pub struct WifSequence<T: Clone + WifValue>(pub Vec<SequenceEntry<T>>);
277
278impl WifSequence<Vec<usize>> {
279    /// Converts a sequence of arrays to a sequence of numbers. Err value is the first index with multiple numbers
280    ///
281    /// # Errors
282    /// The error is the first index with multiple values
283    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/// Iterator for a [`WifSequence`], returns a default when indices are skipped, returns clones of entries
294#[derive(Debug)]
295pub struct SequenceIterDefault<'a, T: Clone + WifValue + Default> {
296    /// index of iterator
297    index: usize,
298    /// index into Vec
299    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/// Iterator for a [`WifSequence`], returns items as Option, returning `Some(None)` on skipped
322/// indices, and `Some(Some(T))` on present ones
323#[derive(Debug)]
324pub struct SequenceIterOption<'a, T: Clone + WifValue> {
325    /// index of iterator
326    index: usize,
327    /// index into Vec
328    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    /// Constructs a sequence from an [`IndexMap`]. Returns a parse error on invalid keys or values
361    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    /// Convert to a map of index to value
395    #[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    /// Same as [`from_array`](Self::from_array) but it accepts [None] in place of 0 values
403    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    /// Returns an owned iterator that returns default values for skipped indices
420    #[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    /// Constructs a new [`WifSequence`] from an array. This sequence will always be valid
432    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    /// Convert to a map of index to value
445    #[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    /// Iterator of entries
451    pub fn entry_iter(&self) -> slice::Iter<SequenceEntry<T>> {
452        self.0.iter()
453    }
454
455    /// Creates an iterator that returns the values in the sequence, wrapped in an option, with `None` for missing values
456    #[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    /// Validates indices within the sequence to ensure that they are non-zero and strictly increasing
466    ///
467    /// # Errors
468    /// Returns an error if indices are out of order.
469    ///
470    /// # Examples
471    /// ```
472    /// # use indexmap::indexmap;
473    /// # use wif_weave::wif::{SequenceError};
474    /// use wif_weave::wif::data::{WifParseable, WifSequence};
475    /// let map = indexmap! {
476    ///     String::from("0") => Some(String::from("1"))
477    /// };
478    /// let sequence = WifSequence::<usize>::from_index_map(&map).0;
479    /// assert_eq!(SequenceError::Zero(0), sequence.validate().unwrap_err());
480    /// ```
481    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
512/// Alias for `Option<ParsedValue<T>>`
513pub type OptionalValue<T> = Option<ParsedValue<T>>;
514
515/// Value parsed from a wif field. If parsing failed, the original value is inside the `Err`
516#[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    /// Get as result
531    ///
532    /// # Errors
533    /// Original parse error and string value
534    pub const fn as_result(&self) -> Result<&T, &(ParseError, Option<String>)> {
535        self.0.as_ref()
536    }
537
538    /// Gets inner value as an option
539    pub fn as_option(&self) -> Option<&T> {
540        self.0.as_ref().ok()
541    }
542
543    /// Get error from result
544    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    /// Get the value as a string. If parsing failed, returns the original string value
569    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/// Allowed units for thread thickness info.
582#[derive(Clone, Debug, EnumString, PartialEq, Eq, Copy, strum::Display)]
583#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
584pub enum ThreadUnit {
585    /// Centimeters
586    Centimeters,
587    /// Inches
588    Inches,
589    /// Decipoints, seems to be 1/10 of a point (as in 12 pt font). 1/720 of an inch.
590    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/// Wrapper for float values in a wif. Guaranteed to be parseable as a `f64`
613///
614/// Since this crate does no calculations, holding the value as a decimal allows for no loss of
615/// precision and leaves the decision of how to handle decimal points to the caller.
616#[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}