1#[macro_use]
2extern crate lazy_static;
3
4extern crate regex;
5
6mod tests;
7
8pub struct Message {
9 pub name: String,
10 pub full_name: String,
11 pub message_type: MessageType,
12 pub number: u8,
13 pub fields: Vec<Field>
14}
15
16#[derive(Copy, Clone)]
17pub enum MessageType {
18 Request,
19 Response
20}
21
22impl MessageType {
23 fn char(self) -> char {
24 match self {
25 MessageType::Request => 'T',
26 MessageType::Response => 'R',
27 }
28 }
29}
30
31pub struct Field {
32 pub name: String,
33 pub times: Option<String>,
34 pub field_type: FieldType
35}
36
37#[derive(Debug, Eq, PartialEq, Copy, Clone)]
38pub enum FieldType {
39 U8,
40 U16,
41 U32,
42 U64,
43 QID,
44 Stat,
45 Bytes,
46 String,
47}
48
49impl FieldType {
50 fn integer(self) -> bool {
51 match self {
52 FieldType::U8 | FieldType::U16 | FieldType::U32 | FieldType::U64 => true,
53 _ => false,
54 }
55 }
56}
57
58impl std::fmt::Display for Message {
59 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
60 try!(write!(f, "size[4] {}:{} tag[2]", self.full_name, self.number));
61 for field in &self.fields {
62 try!(write!(f, " {}", field))
63 }
64 Ok(())
65 }
66}
67
68impl std::fmt::Display for Field {
69 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
70 match self.times {
71 Some(ref times) => write!(f, "{}*({}[{}])", times, self.name, self.field_type),
72 None => write!(f, "{}[{}]", self.name, self.field_type)
73 }
74 }
75}
76
77impl std::fmt::Display for FieldType {
78 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
79 let s = match self {
80 &FieldType::U8 => "1",
81 &FieldType::U16 => "2",
82 &FieldType::U32 => "4",
83 &FieldType::U64 => "8",
84 &FieldType::QID => "13",
85 &FieldType::Stat => "n",
86 &FieldType::Bytes => "count",
87 &FieldType::String => "s"
88 };
89 write!(f, "{}", s)
90 }
91}
92
93macro_rules! re {
94 ($name:ident $pattern:expr) => (
95 lazy_static! {
96 static ref $name: regex::Regex = regex::Regex::new($pattern).unwrap();
97 }
98 )
99}
100
101fn field(input: &str) -> (&str, Option<Field>) {
102 re!{ SCALAR r"^\s*([a-z]+)\s*[[](1|2|4|8|13|count|n|s)]\s*" }
103 re!{ ARRAY r"^\s*([a-z]+)\s*[*]\s*[(]([^)]*)[)]\s*" }
104 if let Some(captures) = SCALAR.captures(input) {
105 let name = captures.at(1).unwrap();
106 let field_type = match captures.at(2).unwrap() {
107 "1" => FieldType::U8,
108 "2" => FieldType::U16,
109 "4" => FieldType::U32,
110 "8" => FieldType::U64,
111 "13" => FieldType::QID,
112 "count" => FieldType::Bytes,
113 "n" => FieldType::Stat,
114 "s" => FieldType::String,
115 _ => return (input, None),
116 };
117 let rest = &input[captures.at(0).unwrap().len()..];
118 (rest, Some(Field{name: name.to_owned(), times: None, field_type: field_type}))
119 } else if let Some(captures) = ARRAY.captures(input) {
120 let times = captures.at(1).unwrap();
121 let field_text = captures.at(2).unwrap();
122 let rest = &input[captures.at(0).unwrap().len()..];
123 if let (_, Some(mut field)) = field(field_text) {
124 field.times = Some(times.to_owned());
125 (rest, Some(field))
126 } else {
127 (input, None)
128 }
129 } else {
130 (input, None)
131 }
132}
133
134fn name(input: &str) -> (&str, Option<(MessageType, &str, u8)>) {
135 re!(NAME r"\s*(T|R)([a-z]+)\s*:\s*([1-9][0-9]*)\s*");
136 if let Some(captures) = NAME.captures(input) {
137 let message_type = match captures.at(1).unwrap() {
138 "T" => MessageType::Request,
139 "R" => MessageType::Response,
140 _ => return (input, None),
141 };
142 let name = captures.at(2).unwrap();
143 let number = captures.at(3).unwrap().parse().unwrap();
144 let rest = &input[captures.at(0).unwrap().len()..];
145 (rest, Some((message_type, name, number)))
146 } else {
147 (input, None)
148 }
149}
150
151fn error(line_number: usize, line: &str, message: &str) -> Result<Vec<Message>, String> {
152 Err(if line_number > 0 {
153 format!("line {}: {}\nerror: {}", line_number, line, message)
154 } else {
155 format!("error: {}", message)
156 })
157}
158
159pub fn strip(definition: &str) -> Vec<(usize, &str)> {
160 definition.lines().enumerate().flat_map(|(i, mut line)| {
161 if let Some(i) = line.find('#') {
162 line = &line[0..i]
163 }
164
165 line = line.trim();
166
167 if line.len() == 0 {
168 None
169 } else {
170 Some((i, line))
171 }
172 }).collect()
173}
174
175pub fn parse(definition: &str) -> Result<Vec<Message>, String> {
176 let mut messages = vec![];
177 let mut names = std::collections::HashSet::<String>::new();
178 let mut numbers = std::collections::HashSet::<u8>::new();
179
180 for (i, line) in strip(definition) {
181 let n = i + 1;
182 let (s, size) = field(line);
183 match size {
184 Some(field) => if field.name != "size" || field.field_type != FieldType::U32 {
185 return error(n, line, "each message must begin with size[4]");
186 },
187 None => return error(n, line, "each message must begin with size[4]"),
188 }
189
190 let (s, name) = name(s);
191 let (message_type, message_name, number) = match name {
192 Some(result) => result,
193 None => return error(n, line, "size tag must be followed by a message name"),
194 };
195
196 let (s, tag) = field(s);
197 match tag {
198 Some(field) => if field.name != "tag" || field.field_type != FieldType::U16 {
199 return error(n, line, "each message must have tag[2] as its first field");
200 },
201 None => return error(n, line, "each message must have tag[2] as its first field"),
202 }
203
204 let mut fields: Vec<Field> = vec![];
205 let mut s = s;
206 while s.len() > 0 {
207 let (rest, option) = field(s);
208 let field = match option {
209 Some(field) => field,
210 _ => return error(n, line, &("bad field in message: ".to_owned() + rest)),
211 };
212 if let Some(ref times) = field.times {
213 if let Some(previous) = fields.last() {
214 if &previous.name != times || !previous.field_type.integer() {
215 return error(n, line, "array field must be preceded by integer times field");
216 }
217 } else {
218 return error(n, line, "array field must be preceded by times field");
219 }
220 };
221 fields.push(field);
222 s = rest;
223 }
224
225 let message = Message {
226 name: message_name.to_owned(),
227 full_name: message_type.char().to_string() + message_name,
228 message_type: message_type,
229 number: number,
230 fields: fields
231 };
232 if names.contains(&message.full_name) {
233 return error(n, line, &format!("duplicate message name: {}", message.full_name));
234 };
235 names.insert(message.full_name.clone());
236 if numbers.contains(&message.number) {
237 let msg = &format!("duplicate message number: {}:{}", message.full_name, message.number);
238 return error(n, line, msg);
239 }
240 numbers.insert(message.number);
241 messages.push(message);
242 }
243
244 {
245 let mut requests = std::collections::HashMap::<String, &Message>::new();
246 let mut responses = std::collections::HashMap::<String, &Message>::new();
247
248 for message in &messages {
249 match message.message_type {
250 MessageType::Request => requests.insert(message.name.clone(), message),
251 MessageType::Response => responses.insert(message.name.clone(), message),
252 };
253 }
254
255 for name in requests.keys() {
256 if !responses.contains_key(name) {
257 return error(0, "", &format!("request without corresponding response: {}", name));
258 }
259 }
260
261 for (name, response) in &responses {
262 match requests.get(name) {
263 Some(request) => if response.number != request.number + 1 {
264 return error(0, "", &format!(
265 "Response number not equal to request number + 1: {}\n{}: {}\n{}\n{}\n",
266 name, request.full_name, request.number, response.full_name, response.number)
267 );
268 },
269 None => if name != "error" {
270 return error(0, "", &format!("response without corresponding request: {}", name));
271 }
272 }
273 }
274 }
275
276 Ok(messages)
277}