scanf/format/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
use std::{
    any::{Any, TypeId},
    io,
};

mod format_parser;
use format_parser::InputFormatToken;

#[derive(Debug, PartialEq, Eq)]
enum InputType {
    Type(TypeId),
    GenericType,
}

impl InputType {
    fn typed<T: ?Sized + Any>() -> Self {
        Self::Type(TypeId::of::<T>())
    }
}

impl<'a> From<&InputFormatToken<'a>> for InputType {
    fn from(input_format: &InputFormatToken<'a>) -> Self {
        match *input_format {
            InputFormatToken::Type(type_id) => Self::Type(type_id),
            InputFormatToken::GenericType => Self::GenericType,
            InputFormatToken::Text(_) => unreachable!("Input format of text is not a placeholder"),
        }
    }
}

pub struct InputElement<'a> {
    input: &'a str,
    required_type: InputType,
}

impl<'a> InputElement<'a> {
    fn new(input: &'a str, required_type: InputType) -> Self {
        Self {
            input: if required_type != InputType::typed::<String>() {
                input.trim()
            } else {
                input
            },
            required_type,
        }
    }

    #[inline]
    pub fn as_str(&self) -> &'a str {
        self.input
    }

    #[inline]
    pub fn is_required_type_of_var<T: ?Sized + Any>(&self, _var: &T) -> bool {
        match self.required_type {
            InputType::GenericType => true,
            InputType::Type(type_id) => type_id == TypeId::of::<T>(),
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
pub struct InputFormatParser<'a> {
    tokens: Vec<InputFormatToken<'a>>,
}

impl<'a> InputFormatParser<'a> {
    pub fn new(input_format: &'a str) -> io::Result<Self> {
        let (_, tokens) = format_parser::tokenize(input_format).map_err(|error| {
            io::Error::new(
                io::ErrorKind::InvalidInput,
                format!("Invalid input format string: {}", error),
            )
        })?;
        return Ok(Self { tokens });
    }

    pub fn inputs(&self, input: &'a str) -> io::Result<Vec<InputElement<'a>>> {
        let mut input = input;
        let mut capture = None;
        let mut input_elements = Vec::new();
        for token in &self.tokens {
            match token {
                &InputFormatToken::Text(text) => {
                    if let Some(text_start_offset) = input.find(text) {
                        if let Some(required_type) = capture {
                            capture = None;
                            let input_text = &input[..text_start_offset];
                            let input_element = InputElement::new(input_text, required_type);
                            input_elements.push(input_element);
                            input = &input[(text_start_offset + text.len())..];
                        } else {
                            input = &input[text.len()..];
                        }
                    } else {
                        return Err(io::Error::new(
                            io::ErrorKind::InvalidInput,
                            format!("Can not find text separator {:?}", text),
                        ));
                    }
                }
                input_placeholder_token => {
                    if capture.is_some() {
                        return Err(io::Error::new(
                            io::ErrorKind::InvalidInput,
                            "Can not split input correctly because the consecutive placeholder",
                        ));
                    }
                    capture = Some(InputType::from(input_placeholder_token));
                }
            }
        }
        if let Some(required_type) = capture {
            let input_element = InputElement::new(input, required_type);
            input_elements.push(input_element);
        }
        return Ok(input_elements);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_formatter_simple_generic() {
        let formatter = InputFormatParser::new("{}").unwrap();
        assert_eq!(formatter.tokens, vec![InputFormatToken::GenericType])
    }

    #[test]
    fn test_formatter_two_generic_with_separator() {
        let formatter = InputFormatParser::new("{} -> {}").unwrap();
        assert_eq!(
            formatter.tokens,
            vec![
                InputFormatToken::GenericType,
                InputFormatToken::Text(" -> "),
                InputFormatToken::GenericType,
            ]
        )
    }

    #[test]
    #[should_panic]
    fn test_wrong_formatter_unescaped_open_bracket() {
        InputFormatParser::new("{} -{> {}").unwrap();
    }

    #[test]
    #[should_panic]
    fn test_wrong_formatter_unescaped_close_bracket() {
        InputFormatParser::new("{} -}> {}").unwrap();
    }

    #[test]
    fn test_formatter_two_generic_without_separator() {
        let formatter = InputFormatParser::new("{}{}").unwrap();
        assert_eq!(
            formatter.tokens,
            vec![InputFormatToken::GenericType, InputFormatToken::GenericType,]
        )
    }

    #[test]
    fn test_formatter_number_and_string_without_separator() {
        let formatter = InputFormatParser::new("{i32}{string}").unwrap();
        assert_eq!(
            formatter.tokens,
            vec![
                InputFormatToken::typed::<i32>(),
                InputFormatToken::typed::<String>(),
            ]
        )
    }
}