/
ch-2.rs
executable file
·123 lines (116 loc) · 3.48 KB
/
ch-2.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
#! /bin/sh
//usr/bin/env rustc --test $0 -o ${0}x && ./${0}x --nocapture; rm -f ${0}x ; exit
use std::collections::{HashMap, VecDeque};
#[derive(PartialEq, Debug)]
pub struct Lump {
id: String,
fields: HashMap<String, String>,
}
#[test]
fn test_ex1() {
assert_eq!(
lineparser(
"{% id field1=\"value1\" field2=\"value2\" field3=42 %}"
),
Lump {
id: "id".to_string(),
fields: HashMap::from([
("field3".to_string(), "42".to_string()),
("field2".to_string(), "value2".to_string()),
("field1".to_string(), "value1".to_string())
])
}
);
}
#[test]
fn test_ex2() {
assert_eq!(
lineparser("{% youtube title=\"Title \\\"quoted\\\" done\" %}"),
Lump {
id: "youtube".to_string(),
fields: HashMap::from([(
"title".to_string(),
"Title \"quoted\" done".to_string()
)])
}
);
}
#[test]
fn test_ex3() {
assert_eq!(
lineparser(
"{% youtube title=\"Title with escaped backslash \\\\\" %}"
),
Lump {
id: "youtube".to_string(),
fields: HashMap::from([(
"title".to_string(),
"Title with escaped backslash \\".to_string()
)])
}
);
}
#[derive(PartialEq, Debug)]
enum State {
Outside,
PreID,
InID,
InterField,
FieldName,
FieldValue,
FieldValueQuoted,
}
fn lineparser(line: &str) -> Lump {
let mut l = line.chars().collect::<VecDeque<_>>();
let mut state = State::Outside;
let mut trail: Vec<char> = Vec::new();
let mut fieldname = "".to_string();
let mut out = Lump { id: "".to_string(), fields: HashMap::new() };
while l.len() > 0 {
let mut c = l.pop_front().unwrap();
if state == State::Outside && c == '{' {
c = l.pop_front().unwrap();
if c == '%' {
state = State::PreID;
}
} else if (state == State::PreID || state == State::InID) && c != ' ' {
trail.push(c);
state = State::InID;
} else if state == State::InID && c == ' ' {
out.id = trail.into_iter().collect();
trail = Vec::new();
state = State::InterField;
} else if (state == State::InterField || state == State::FieldName)
&& c != ' '
&& c != '='
&& c != '%'
{
trail.push(c);
state = State::FieldName;
} else if state == State::FieldName && c == '=' {
fieldname = trail.into_iter().collect();
trail = Vec::new();
state = State::FieldValue;
} else if state == State::FieldValue && trail.len() == 0 && c == '"' {
state = State::FieldValueQuoted;
} else if state == State::FieldValue || state == State::FieldValueQuoted
{
let mut literal = false;
if c == '\\' {
c = l.pop_front().unwrap();
literal = true;
}
if (c == ' ' && state == State::FieldValue)
|| (c == '"' && state == State::FieldValueQuoted && !literal)
{
out.fields
.insert(fieldname.clone(), trail.into_iter().collect());
trail = Vec::new();
state = State::InterField;
} else {
trail.push(c);
}
}
}
out
}