1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 use crate::preferences::{Pref, PrefValue, Preferences};
6 use std::borrow::Borrow;
7 use std::borrow::Cow;
8 use std::char;
9 use std::error::Error;
10 use std::fmt;
11 use std::io::{self, Write};
12 use std::iter::Iterator;
13 use std::mem;
14 use std::str;
15
16 impl PrefReaderError {
new( message: &'static str, position: Position, parent: Option<Box<dyn Error>>, ) -> PrefReaderError17 fn new(
18 message: &'static str,
19 position: Position,
20 parent: Option<Box<dyn Error>>,
21 ) -> PrefReaderError {
22 PrefReaderError {
23 message,
24 position,
25 parent,
26 }
27 }
28 }
29
30 impl fmt::Display for PrefReaderError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result31 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32 write!(
33 f,
34 "{} at line {}, column {}",
35 self.message, self.position.line, self.position.column
36 )
37 }
38 }
39
40 impl Error for PrefReaderError {
description(&self) -> &str41 fn description(&self) -> &str {
42 self.message
43 }
44
cause(&self) -> Option<&dyn Error>45 fn cause(&self) -> Option<&dyn Error> {
46 self.parent.as_deref()
47 }
48 }
49
50 impl From<io::Error> for PrefReaderError {
from(err: io::Error) -> PrefReaderError51 fn from(err: io::Error) -> PrefReaderError {
52 PrefReaderError::new("IOError", Position::new(), Some(err.into()))
53 }
54 }
55
56 #[derive(Copy, Clone, Debug, PartialEq)]
57 enum TokenizerState {
58 Junk,
59 CommentStart,
60 CommentLine,
61 CommentBlock,
62 FunctionName,
63 AfterFunctionName,
64 FunctionArgs,
65 FunctionArg,
66 DoubleQuotedString,
67 SingleQuotedString,
68 Number,
69 Bool,
70 AfterFunctionArg,
71 AfterFunction,
72 Error,
73 }
74
75 #[derive(Copy, Clone, Debug, Default, PartialEq)]
76 pub struct Position {
77 line: u32,
78 column: u32,
79 }
80
81 impl Position {
new() -> Position82 pub fn new() -> Position {
83 Position { line: 1, column: 0 }
84 }
85 }
86
87 #[derive(Copy, Clone, Debug, PartialEq)]
88 pub enum TokenType {
89 None,
90 PrefFunction,
91 UserPrefFunction,
92 StickyPrefFunction,
93 CommentBlock,
94 CommentLine,
95 CommentBashLine,
96 Paren,
97 Semicolon,
98 Comma,
99 String,
100 Int,
101 Bool,
102 Error,
103 }
104
105 #[derive(Debug, PartialEq)]
106 pub enum PrefToken<'a> {
107 PrefFunction(Position),
108 UserPrefFunction(Position),
109 StickyPrefFunction(Position),
110 CommentBlock(Cow<'a, str>, Position),
111 CommentLine(Cow<'a, str>, Position),
112 CommentBashLine(Cow<'a, str>, Position),
113 Paren(char, Position),
114 Semicolon(Position),
115 Comma(Position),
116 String(Cow<'a, str>, Position),
117 Int(i64, Position),
118 Bool(bool, Position),
119 Error(&'static str, Position),
120 }
121
122 impl<'a> PrefToken<'a> {
position(&self) -> Position123 fn position(&self) -> Position {
124 match *self {
125 PrefToken::PrefFunction(position) => position,
126 PrefToken::UserPrefFunction(position) => position,
127 PrefToken::StickyPrefFunction(position) => position,
128 PrefToken::CommentBlock(_, position) => position,
129 PrefToken::CommentLine(_, position) => position,
130 PrefToken::CommentBashLine(_, position) => position,
131 PrefToken::Paren(_, position) => position,
132 PrefToken::Semicolon(position) => position,
133 PrefToken::Comma(position) => position,
134 PrefToken::String(_, position) => position,
135 PrefToken::Int(_, position) => position,
136 PrefToken::Bool(_, position) => position,
137 PrefToken::Error(_, position) => position,
138 }
139 }
140 }
141
142 #[derive(Debug)]
143 pub struct PrefReaderError {
144 message: &'static str,
145 position: Position,
146 parent: Option<Box<dyn Error>>,
147 }
148
149 struct TokenData<'a> {
150 token_type: TokenType,
151 complete: bool,
152 position: Position,
153 data: Cow<'a, str>,
154 start_pos: usize,
155 }
156
157 impl<'a> TokenData<'a> {
new(token_type: TokenType, position: Position, start_pos: usize) -> TokenData<'a>158 fn new(token_type: TokenType, position: Position, start_pos: usize) -> TokenData<'a> {
159 TokenData {
160 token_type,
161 complete: false,
162 position,
163 data: Cow::Borrowed(""),
164 start_pos,
165 }
166 }
167
start(&mut self, tokenizer: &PrefTokenizer, token_type: TokenType)168 fn start(&mut self, tokenizer: &PrefTokenizer, token_type: TokenType) {
169 self.token_type = token_type;
170 self.position = tokenizer.position;
171 self.start_pos = tokenizer.pos;
172 }
173
end(&mut self, buf: &'a [u8], end_pos: usize) -> Result<(), PrefReaderError>174 fn end(&mut self, buf: &'a [u8], end_pos: usize) -> Result<(), PrefReaderError> {
175 self.complete = true;
176 self.add_slice_to_token(buf, end_pos)
177 }
178
add_slice_to_token(&mut self, buf: &'a [u8], end_pos: usize) -> Result<(), PrefReaderError>179 fn add_slice_to_token(&mut self, buf: &'a [u8], end_pos: usize) -> Result<(), PrefReaderError> {
180 let data = match str::from_utf8(&buf[self.start_pos..end_pos]) {
181 Ok(x) => x,
182 Err(_) => {
183 return Err(PrefReaderError::new(
184 "Could not convert string to utf8",
185 self.position,
186 None,
187 ));
188 }
189 };
190 if self.data != "" {
191 self.data.to_mut().push_str(&data)
192 } else {
193 self.data = Cow::Borrowed(&data)
194 };
195 Ok(())
196 }
197
push_char(&mut self, tokenizer: &PrefTokenizer, data: char)198 fn push_char(&mut self, tokenizer: &PrefTokenizer, data: char) {
199 self.data.to_mut().push(data);
200 self.start_pos = tokenizer.pos + 1;
201 }
202 }
203
204 pub struct PrefTokenizer<'a> {
205 data: &'a [u8],
206 pos: usize,
207 cur: Option<char>,
208 position: Position,
209 state: TokenizerState,
210 next_state: Option<TokenizerState>,
211 }
212
213 impl<'a> PrefTokenizer<'a> {
new(data: &'a [u8]) -> PrefTokenizer<'a>214 pub fn new(data: &'a [u8]) -> PrefTokenizer<'a> {
215 PrefTokenizer {
216 data,
217 pos: 0,
218 cur: None,
219 position: Position::new(),
220 state: TokenizerState::Junk,
221 next_state: Some(TokenizerState::FunctionName),
222 }
223 }
224
make_token(&mut self, token_data: TokenData<'a>) -> PrefToken<'a>225 fn make_token(&mut self, token_data: TokenData<'a>) -> PrefToken<'a> {
226 let buf = token_data.data;
227 let position = token_data.position;
228 match token_data.token_type {
229 TokenType::None => panic!("Got a token without a type"),
230 TokenType::PrefFunction => PrefToken::PrefFunction(position),
231 TokenType::UserPrefFunction => PrefToken::UserPrefFunction(position),
232 TokenType::StickyPrefFunction => PrefToken::StickyPrefFunction(position),
233 TokenType::CommentBlock => PrefToken::CommentBlock(buf, position),
234 TokenType::CommentLine => PrefToken::CommentLine(buf, position),
235 TokenType::CommentBashLine => PrefToken::CommentBashLine(buf, position),
236 TokenType::Paren => {
237 if buf.len() != 1 {
238 panic!("Expected a buffer of length one");
239 }
240 PrefToken::Paren(buf.chars().next().unwrap(), position)
241 }
242 TokenType::Semicolon => PrefToken::Semicolon(position),
243 TokenType::Comma => PrefToken::Comma(position),
244 TokenType::String => PrefToken::String(buf, position),
245 TokenType::Int => {
246 let value = buf.parse::<i64>().expect("Integer wasn't parsed as an i64");
247 PrefToken::Int(value, position)
248 }
249 TokenType::Bool => {
250 let value = match buf.borrow() {
251 "true" => true,
252 "false" => false,
253 x => panic!("Boolean wasn't 'true' or 'false' (was {})", x),
254 };
255 PrefToken::Bool(value, position)
256 }
257 TokenType::Error => panic!("make_token can't construct errors"),
258 }
259 }
260
get_char(&mut self) -> Option<char>261 fn get_char(&mut self) -> Option<char> {
262 if self.pos >= self.data.len() - 1 {
263 self.cur = None;
264 return None;
265 };
266 if self.cur.is_some() {
267 self.pos += 1;
268 }
269 let c = self.data[self.pos] as char;
270 if self.cur == Some('\n') {
271 self.position.line += 1;
272 self.position.column = 0;
273 } else if self.cur.is_some() {
274 self.position.column += 1;
275 };
276 self.cur = Some(c);
277 self.cur
278 }
279
unget_char(&mut self) -> Option<char>280 fn unget_char(&mut self) -> Option<char> {
281 if self.pos == 0 {
282 self.position.column = 0;
283 self.cur = None
284 } else {
285 self.pos -= 1;
286 let c = self.data[self.pos] as char;
287 if c == '\n' {
288 self.position.line -= 1;
289 let mut col_pos = self.pos - 1;
290 while col_pos > 0 && self.data[col_pos] as char != '\n' {
291 col_pos -= 1;
292 }
293 self.position.column = (self.pos - col_pos as usize - 1) as u32;
294 } else {
295 self.position.column -= 1;
296 }
297 self.cur = Some(c);
298 }
299 self.cur
300 }
301
is_space(c: char) -> bool302 fn is_space(c: char) -> bool {
303 matches!(c, ' ' | '\t' | '\r' | '\n')
304 }
305
skip_whitespace(&mut self) -> Option<char>306 fn skip_whitespace(&mut self) -> Option<char> {
307 while let Some(c) = self.cur {
308 if PrefTokenizer::is_space(c) {
309 self.get_char();
310 } else {
311 break;
312 };
313 }
314 self.cur
315 }
316
consume_escape(&mut self, token_data: &mut TokenData<'a>) -> Result<(), PrefReaderError>317 fn consume_escape(&mut self, token_data: &mut TokenData<'a>) -> Result<(), PrefReaderError> {
318 let pos = self.pos;
319 let escaped = self.read_escape()?;
320 if let Some(escape_char) = escaped {
321 token_data.add_slice_to_token(&self.data, pos)?;
322 token_data.push_char(&self, escape_char);
323 };
324 Ok(())
325 }
326
read_escape(&mut self) -> Result<Option<char>, PrefReaderError>327 fn read_escape(&mut self) -> Result<Option<char>, PrefReaderError> {
328 let escape_char = match self.get_char() {
329 Some('u') => self.read_hex_escape(4, true)?,
330 Some('x') => self.read_hex_escape(2, true)?,
331 Some('\\') => '\\' as u32,
332 Some('"') => '"' as u32,
333 Some('\'') => '\'' as u32,
334 Some('r') => '\r' as u32,
335 Some('n') => '\n' as u32,
336 Some(_) => return Ok(None),
337 None => {
338 return Err(PrefReaderError::new(
339 "EOF in character escape",
340 self.position,
341 None,
342 ))
343 }
344 };
345 Ok(Some(char::from_u32(escape_char).ok_or_else(|| {
346 PrefReaderError::new("Invalid codepoint decoded from escape", self.position, None)
347 })?))
348 }
349
read_hex_escape(&mut self, hex_chars: isize, first: bool) -> Result<u32, PrefReaderError>350 fn read_hex_escape(&mut self, hex_chars: isize, first: bool) -> Result<u32, PrefReaderError> {
351 let mut value = 0;
352 for _ in 0..hex_chars {
353 match self.get_char() {
354 Some(x) => {
355 value <<= 4;
356 match x {
357 '0'..='9' => value += x as u32 - '0' as u32,
358 'a'..='f' => value += x as u32 - 'a' as u32,
359 'A'..='F' => value += x as u32 - 'A' as u32,
360 _ => {
361 return Err(PrefReaderError::new(
362 "Unexpected character in escape",
363 self.position,
364 None,
365 ))
366 }
367 }
368 }
369 None => {
370 return Err(PrefReaderError::new(
371 "Unexpected EOF in escape",
372 self.position,
373 None,
374 ))
375 }
376 }
377 }
378 if first && (0xD800..=0xDBFF).contains(&value) {
379 // First part of a surrogate pair
380 if self.get_char() != Some('\\') || self.get_char() != Some('u') {
381 return Err(PrefReaderError::new(
382 "Lone high surrogate in surrogate pair",
383 self.position,
384 None,
385 ));
386 }
387 self.unget_char();
388 let high_surrogate = value;
389 let low_surrogate = self.read_hex_escape(4, false)?;
390 let high_value = (high_surrogate - 0xD800) << 10;
391 let low_value = low_surrogate - 0xDC00;
392 value = high_value + low_value + 0x10000;
393 } else if first && (0xDC00..=0xDFFF).contains(&value) {
394 return Err(PrefReaderError::new(
395 "Lone low surrogate",
396 self.position,
397 None,
398 ));
399 } else if !first && !(0xDC00..=0xDFFF).contains(&value) {
400 return Err(PrefReaderError::new(
401 "Invalid low surrogate in surrogate pair",
402 self.position,
403 None,
404 ));
405 }
406 Ok(value)
407 }
408
get_match(&mut self, target: &str, separators: &str) -> bool409 fn get_match(&mut self, target: &str, separators: &str) -> bool {
410 let initial_pos = self.pos;
411 let mut matched = true;
412 for c in target.chars() {
413 if self.cur == Some(c) {
414 self.get_char();
415 } else {
416 matched = false;
417 break;
418 }
419 }
420
421 if !matched {
422 for _ in 0..(self.pos - initial_pos) {
423 self.unget_char();
424 }
425 } else {
426 // Check that the next character is whitespace or a separator
427 if let Some(c) = self.cur {
428 if !(PrefTokenizer::is_space(c) || separators.contains(c) || c == '/') {
429 matched = false;
430 }
431 }
432 self.unget_char();
433 }
434
435 matched
436 }
437
next_token(&mut self) -> Result<Option<TokenData<'a>>, PrefReaderError>438 fn next_token(&mut self) -> Result<Option<TokenData<'a>>, PrefReaderError> {
439 let mut token_data = TokenData::new(TokenType::None, Position::new(), 0);
440
441 loop {
442 let mut c = match self.get_char() {
443 Some(x) => x,
444 None => return Ok(None),
445 };
446
447 self.state = match self.state {
448 TokenizerState::Junk => {
449 c = match self.skip_whitespace() {
450 Some(x) => x,
451 None => return Ok(None),
452 };
453 match c {
454 '/' => TokenizerState::CommentStart,
455 '#' => {
456 token_data.start(&self, TokenType::CommentBashLine);
457 token_data.start_pos = self.pos + 1;
458 TokenizerState::CommentLine
459 }
460 _ => {
461 self.unget_char();
462 let next = match self.next_state {
463 Some(x) => x,
464 None => {
465 return Err(PrefReaderError::new(
466 "In Junk state without a next state defined",
467 self.position,
468 None,
469 ))
470 }
471 };
472 self.next_state = None;
473 next
474 }
475 }
476 }
477 TokenizerState::CommentStart => match c {
478 '*' => {
479 token_data.start(&self, TokenType::CommentBlock);
480 token_data.start_pos = self.pos + 1;
481 TokenizerState::CommentBlock
482 }
483 '/' => {
484 token_data.start(&self, TokenType::CommentLine);
485 token_data.start_pos = self.pos + 1;
486 TokenizerState::CommentLine
487 }
488 _ => {
489 return Err(PrefReaderError::new(
490 "Invalid character after /",
491 self.position,
492 None,
493 ))
494 }
495 },
496 TokenizerState::CommentLine => match c {
497 '\n' => {
498 token_data.end(&self.data, self.pos)?;
499 TokenizerState::Junk
500 }
501 _ => TokenizerState::CommentLine,
502 },
503 TokenizerState::CommentBlock => match c {
504 '*' => {
505 if self.get_char() == Some('/') {
506 token_data.end(&self.data, self.pos - 1)?;
507 TokenizerState::Junk
508 } else {
509 TokenizerState::CommentBlock
510 }
511 }
512 _ => TokenizerState::CommentBlock,
513 },
514 TokenizerState::FunctionName => {
515 let position = self.position;
516 let start_pos = self.pos;
517 match c {
518 'u' => {
519 if self.get_match("user_pref", "(") {
520 token_data.start(&self, TokenType::UserPrefFunction);
521 }
522 }
523 's' => {
524 if self.get_match("sticky_pref", "(") {
525 token_data.start(&self, TokenType::StickyPrefFunction);
526 }
527 }
528 'p' => {
529 if self.get_match("pref", "(") {
530 token_data.start(&self, TokenType::PrefFunction);
531 }
532 }
533 _ => {}
534 };
535 if token_data.token_type == TokenType::None {
536 // We didn't match anything
537 return Err(PrefReaderError::new(
538 "Expected a pref function name",
539 position,
540 None,
541 ));
542 } else {
543 token_data.start_pos = start_pos;
544 token_data.position = position;
545 token_data.end(&self.data, self.pos + 1)?;
546 self.next_state = Some(TokenizerState::AfterFunctionName);
547 TokenizerState::Junk
548 }
549 }
550 TokenizerState::AfterFunctionName => match c {
551 '(' => {
552 self.next_state = Some(TokenizerState::FunctionArgs);
553 token_data.start(&self, TokenType::Paren);
554 token_data.end(&self.data, self.pos + 1)?;
555 self.next_state = Some(TokenizerState::FunctionArgs);
556 TokenizerState::Junk
557 }
558 _ => {
559 return Err(PrefReaderError::new(
560 "Expected an opening paren",
561 self.position,
562 None,
563 ))
564 }
565 },
566 TokenizerState::FunctionArgs => match c {
567 ')' => {
568 token_data.start(&self, TokenType::Paren);
569 token_data.end(&self.data, self.pos + 1)?;
570 self.next_state = Some(TokenizerState::AfterFunction);
571 TokenizerState::Junk
572 }
573 _ => {
574 self.unget_char();
575 TokenizerState::FunctionArg
576 }
577 },
578 TokenizerState::FunctionArg => match c {
579 '"' => {
580 token_data.start(&self, TokenType::String);
581 token_data.start_pos = self.pos + 1;
582 TokenizerState::DoubleQuotedString
583 }
584 '\'' => {
585 token_data.start(&self, TokenType::String);
586 token_data.start_pos = self.pos + 1;
587 TokenizerState::SingleQuotedString
588 }
589 't' | 'f' => {
590 self.unget_char();
591 TokenizerState::Bool
592 }
593 '0'..='9' | '-' | '+' => {
594 token_data.start(&self, TokenType::Int);
595 TokenizerState::Number
596 }
597 _ => {
598 return Err(PrefReaderError::new(
599 "Invalid character at start of function argument",
600 self.position,
601 None,
602 ))
603 }
604 },
605 TokenizerState::DoubleQuotedString => match c {
606 '"' => {
607 token_data.end(&self.data, self.pos)?;
608 self.next_state = Some(TokenizerState::AfterFunctionArg);
609 TokenizerState::Junk
610 }
611 '\n' => {
612 return Err(PrefReaderError::new(
613 "EOL in double quoted string",
614 self.position,
615 None,
616 ))
617 }
618 '\\' => {
619 self.consume_escape(&mut token_data)?;
620 TokenizerState::DoubleQuotedString
621 }
622 _ => TokenizerState::DoubleQuotedString,
623 },
624 TokenizerState::SingleQuotedString => match c {
625 '\'' => {
626 token_data.end(&self.data, self.pos)?;
627 self.next_state = Some(TokenizerState::AfterFunctionArg);
628 TokenizerState::Junk
629 }
630 '\n' => {
631 return Err(PrefReaderError::new(
632 "EOL in single quoted string",
633 self.position,
634 None,
635 ))
636 }
637 '\\' => {
638 self.consume_escape(&mut token_data)?;
639 TokenizerState::SingleQuotedString
640 }
641 _ => TokenizerState::SingleQuotedString,
642 },
643 TokenizerState::Number => match c {
644 '0'..='9' => TokenizerState::Number,
645 ')' | ',' => {
646 token_data.end(&self.data, self.pos)?;
647 self.unget_char();
648 self.next_state = Some(TokenizerState::AfterFunctionArg);
649 TokenizerState::Junk
650 }
651 x if PrefTokenizer::is_space(x) => {
652 token_data.end(&self.data, self.pos)?;
653 self.next_state = Some(TokenizerState::AfterFunctionArg);
654 TokenizerState::Junk
655 }
656 _ => {
657 return Err(PrefReaderError::new(
658 "Invalid character in number literal",
659 self.position,
660 None,
661 ))
662 }
663 },
664 TokenizerState::Bool => {
665 let start_pos = self.pos;
666 let position = self.position;
667 match c {
668 't' => {
669 if self.get_match("true", ",)") {
670 token_data.start(&self, TokenType::Bool)
671 }
672 }
673 'f' => {
674 if self.get_match("false", ",)") {
675 token_data.start(&self, TokenType::Bool)
676 }
677 }
678 _ => {}
679 };
680 if token_data.token_type == TokenType::None {
681 return Err(PrefReaderError::new(
682 "Unexpected characters in function argument",
683 position,
684 None,
685 ));
686 } else {
687 token_data.start_pos = start_pos;
688 token_data.position = position;
689 token_data.end(&self.data, self.pos + 1)?;
690 self.next_state = Some(TokenizerState::AfterFunctionArg);
691 TokenizerState::Junk
692 }
693 }
694 TokenizerState::AfterFunctionArg => match c {
695 ',' => {
696 token_data.start(&self, TokenType::Comma);
697 token_data.end(&self.data, self.pos + 1)?;
698 self.next_state = Some(TokenizerState::FunctionArg);
699 TokenizerState::Junk
700 }
701 ')' => {
702 token_data.start(&self, TokenType::Paren);
703 token_data.end(&self.data, self.pos + 1)?;
704 self.next_state = Some(TokenizerState::AfterFunction);
705 TokenizerState::Junk
706 }
707 _ => {
708 return Err(PrefReaderError::new(
709 "Unexpected character after function argument",
710 self.position,
711 None,
712 ))
713 }
714 },
715 TokenizerState::AfterFunction => match c {
716 ';' => {
717 token_data.start(&self, TokenType::Semicolon);
718 token_data.end(&self.data, self.pos)?;
719 self.next_state = Some(TokenizerState::FunctionName);
720 TokenizerState::Junk
721 }
722 _ => {
723 return Err(PrefReaderError::new(
724 "Unexpected character after function",
725 self.position,
726 None,
727 ))
728 }
729 },
730 TokenizerState::Error => TokenizerState::Error,
731 };
732 if token_data.complete {
733 return Ok(Some(token_data));
734 }
735 }
736 }
737 }
738
739 impl<'a> Iterator for PrefTokenizer<'a> {
740 type Item = PrefToken<'a>;
741
next(&mut self) -> Option<PrefToken<'a>>742 fn next(&mut self) -> Option<PrefToken<'a>> {
743 if let TokenizerState::Error = self.state {
744 return None;
745 }
746 let token_data = match self.next_token() {
747 Err(e) => {
748 self.state = TokenizerState::Error;
749 return Some(PrefToken::Error(e.message, e.position));
750 }
751 Ok(Some(token_data)) => token_data,
752 Ok(None) => return None,
753 };
754 let token = self.make_token(token_data);
755 Some(token)
756 }
757 }
758
tokenize(data: &[u8]) -> PrefTokenizer759 pub fn tokenize(data: &[u8]) -> PrefTokenizer {
760 PrefTokenizer::new(data)
761 }
762
serialize_token<T: Write>(token: &PrefToken, output: &mut T) -> Result<(), PrefReaderError>763 pub fn serialize_token<T: Write>(token: &PrefToken, output: &mut T) -> Result<(), PrefReaderError> {
764 let mut data_buf = String::new();
765
766 let data = match *token {
767 PrefToken::PrefFunction(_) => "pref",
768 PrefToken::UserPrefFunction(_) => "user_pref",
769 PrefToken::StickyPrefFunction(_) => "sticky_pref",
770 PrefToken::CommentBlock(ref data, _) => {
771 data_buf.reserve(data.len() + 4);
772 data_buf.push_str("/*");
773 data_buf.push_str(data.borrow());
774 data_buf.push('*');
775 &*data_buf
776 }
777 PrefToken::CommentLine(ref data, _) => {
778 data_buf.reserve(data.len() + 2);
779 data_buf.push_str("//");
780 data_buf.push_str(data.borrow());
781 &*data_buf
782 }
783 PrefToken::CommentBashLine(ref data, _) => {
784 data_buf.reserve(data.len() + 1);
785 data_buf.push('#');
786 data_buf.push_str(data.borrow());
787 &*data_buf
788 }
789 PrefToken::Paren(data, _) => {
790 data_buf.push(data);
791 &*data_buf
792 }
793 PrefToken::Comma(_) => ",",
794 PrefToken::Semicolon(_) => ";\n",
795 PrefToken::String(ref data, _) => {
796 data_buf.reserve(data.len() + 2);
797 data_buf.push('"');
798 data_buf.push_str(escape_quote(data.borrow()).borrow());
799 data_buf.push('"');
800 &*data_buf
801 }
802 PrefToken::Int(data, _) => {
803 data_buf.push_str(&*data.to_string());
804 &*data_buf
805 }
806 PrefToken::Bool(data, _) => {
807 if data {
808 "true"
809 } else {
810 "false"
811 }
812 }
813 PrefToken::Error(data, pos) => return Err(PrefReaderError::new(data, pos, None)),
814 };
815 output.write_all(data.as_bytes())?;
816 Ok(())
817 }
818
serialize_tokens<'a, I, W>(tokens: I, output: &mut W) -> Result<(), PrefReaderError> where I: Iterator<Item = &'a PrefToken<'a>>, W: Write,819 pub fn serialize_tokens<'a, I, W>(tokens: I, output: &mut W) -> Result<(), PrefReaderError>
820 where
821 I: Iterator<Item = &'a PrefToken<'a>>,
822 W: Write,
823 {
824 for token in tokens {
825 serialize_token(token, output)?;
826 }
827 Ok(())
828 }
829
escape_quote(data: &str) -> Cow<str>830 fn escape_quote(data: &str) -> Cow<str> {
831 // Not very efficient…
832 if data.contains('"') || data.contains('\\') {
833 Cow::Owned(data.replace(r#"\"#, r#"\\"#).replace(r#"""#, r#"\""#))
834 } else {
835 Cow::Borrowed(data)
836 }
837 }
838
839 #[derive(Debug, PartialEq)]
840 enum ParserState {
841 Function,
842 Key,
843 Value,
844 }
845
846 struct PrefBuilder {
847 key: Option<String>,
848 value: Option<PrefValue>,
849 sticky: bool,
850 }
851
852 impl PrefBuilder {
new() -> PrefBuilder853 fn new() -> PrefBuilder {
854 PrefBuilder {
855 key: None,
856 value: None,
857 sticky: false,
858 }
859 }
860 }
861
skip_comments<'a>(tokenizer: &mut PrefTokenizer<'a>) -> Option<PrefToken<'a>>862 fn skip_comments<'a>(tokenizer: &mut PrefTokenizer<'a>) -> Option<PrefToken<'a>> {
863 loop {
864 match tokenizer.next() {
865 Some(PrefToken::CommentBashLine(_, _))
866 | Some(PrefToken::CommentBlock(_, _))
867 | Some(PrefToken::CommentLine(_, _)) => {}
868 Some(x) => return Some(x),
869 None => return None,
870 }
871 }
872 }
873
parse_tokens(tokenizer: &mut PrefTokenizer<'_>) -> Result<Preferences, PrefReaderError>874 pub fn parse_tokens(tokenizer: &mut PrefTokenizer<'_>) -> Result<Preferences, PrefReaderError> {
875 let mut state = ParserState::Function;
876 let mut current_pref = PrefBuilder::new();
877 let mut rv = Preferences::new();
878
879 loop {
880 // Not just using a for loop here seems strange, but this restricts the
881 // scope of the borrow
882 let token = {
883 match tokenizer.next() {
884 Some(x) => x,
885 None => break,
886 }
887 };
888 // First deal with comments and errors
889 match token {
890 PrefToken::Error(msg, position) => {
891 return Err(PrefReaderError::new(msg, position, None));
892 }
893 PrefToken::CommentBashLine(_, _)
894 | PrefToken::CommentLine(_, _)
895 | PrefToken::CommentBlock(_, _) => continue,
896 _ => {}
897 }
898 state = match state {
899 ParserState::Function => {
900 match token {
901 PrefToken::PrefFunction(_) => {
902 current_pref.sticky = false;
903 }
904 PrefToken::UserPrefFunction(_) => {
905 current_pref.sticky = false;
906 }
907 PrefToken::StickyPrefFunction(_) => {
908 current_pref.sticky = true;
909 }
910 _ => {
911 return Err(PrefReaderError::new(
912 "Expected pref function",
913 token.position(),
914 None,
915 ));
916 }
917 }
918 let next = skip_comments(tokenizer);
919 match next {
920 Some(PrefToken::Paren('(', _)) => ParserState::Key,
921 _ => {
922 return Err(PrefReaderError::new(
923 "Expected open paren",
924 next.map(|x| x.position()).unwrap_or(tokenizer.position),
925 None,
926 ))
927 }
928 }
929 }
930 ParserState::Key => {
931 match token {
932 PrefToken::String(data, _) => current_pref.key = Some(data.into_owned()),
933 _ => {
934 return Err(PrefReaderError::new(
935 "Expected string",
936 token.position(),
937 None,
938 ));
939 }
940 }
941 let next = skip_comments(tokenizer);
942 match next {
943 Some(PrefToken::Comma(_)) => ParserState::Value,
944 _ => {
945 return Err(PrefReaderError::new(
946 "Expected comma",
947 next.map(|x| x.position()).unwrap_or(tokenizer.position),
948 None,
949 ))
950 }
951 }
952 }
953 ParserState::Value => {
954 match token {
955 PrefToken::String(data, _) => {
956 current_pref.value = Some(PrefValue::String(data.into_owned()))
957 }
958 PrefToken::Int(data, _) => current_pref.value = Some(PrefValue::Int(data)),
959 PrefToken::Bool(data, _) => current_pref.value = Some(PrefValue::Bool(data)),
960 _ => {
961 return Err(PrefReaderError::new(
962 "Expected value",
963 token.position(),
964 None,
965 ))
966 }
967 }
968 let next = skip_comments(tokenizer);
969 match next {
970 Some(PrefToken::Paren(')', _)) => {}
971 _ => {
972 return Err(PrefReaderError::new(
973 "Expected close paren",
974 next.map(|x| x.position()).unwrap_or(tokenizer.position),
975 None,
976 ))
977 }
978 }
979 let next = skip_comments(tokenizer);
980 match next {
981 Some(PrefToken::Semicolon(_)) | None => {}
982 _ => {
983 return Err(PrefReaderError::new(
984 "Expected semicolon",
985 next.map(|x| x.position()).unwrap_or(tokenizer.position),
986 None,
987 ))
988 }
989 }
990 let key = mem::replace(&mut current_pref.key, None);
991 let value = mem::replace(&mut current_pref.value, None);
992 let pref = if current_pref.sticky {
993 Pref::new_sticky(value.unwrap())
994 } else {
995 Pref::new(value.unwrap())
996 };
997 rv.insert(key.unwrap(), pref);
998 current_pref.sticky = false;
999 ParserState::Function
1000 }
1001 }
1002 }
1003 match state {
1004 ParserState::Key | ParserState::Value => {
1005 return Err(PrefReaderError::new(
1006 "EOF in middle of function",
1007 tokenizer.position,
1008 None,
1009 ));
1010 }
1011 _ => {}
1012 }
1013 Ok(rv)
1014 }
1015
serialize<W: Write>(prefs: &Preferences, output: &mut W) -> io::Result<()>1016 pub fn serialize<W: Write>(prefs: &Preferences, output: &mut W) -> io::Result<()> {
1017 let mut p: Vec<_> = prefs.iter().collect();
1018 p.sort_by(|a, b| a.0.cmp(&b.0));
1019 for &(key, pref) in &p {
1020 let func = if pref.sticky {
1021 "sticky_pref("
1022 } else {
1023 "user_pref("
1024 }
1025 .as_bytes();
1026 output.write_all(func)?;
1027 output.write_all(b"\"")?;
1028 output.write_all(escape_quote(key).as_bytes())?;
1029 output.write_all(b"\"")?;
1030 output.write_all(b", ")?;
1031 match pref.value {
1032 PrefValue::Bool(x) => {
1033 output.write_all(if x { b"true" } else { b"false" })?;
1034 }
1035 PrefValue::Int(x) => {
1036 output.write_all(x.to_string().as_bytes())?;
1037 }
1038 PrefValue::String(ref x) => {
1039 output.write_all(b"\"")?;
1040 output.write_all(escape_quote(x).as_bytes())?;
1041 output.write_all(b"\"")?;
1042 }
1043 };
1044 output.write_all(b");\n")?;
1045 }
1046 Ok(())
1047 }
1048
parse(data: &[u8]) -> Result<Preferences, PrefReaderError>1049 pub fn parse(data: &[u8]) -> Result<Preferences, PrefReaderError> {
1050 let mut tokenizer = tokenize(data);
1051 parse_tokens(&mut tokenizer)
1052 }
1053