use crate::ast::{
Base64, Comment, File, Hex, KeyValue, LineTerminator, Regex, SourceInfo, Whitespace,
};
use crate::combinator::{one_or_more, optional, recover, zero_or_more};
use crate::parser::string::unquoted_template;
use crate::parser::{ParseError, ParseErrorKind, ParseResult, base64, filename, key_string};
use crate::reader::Reader;
use crate::types::{SourceString, ToSource};
pub fn space(reader: &mut Reader) -> ParseResult<Whitespace> {
let start = reader.cursor();
match reader.read() {
None => Err(ParseError::new(start.pos, true, ParseErrorKind::Space)),
Some(c) => {
if c == ' ' || c == '\t' {
Ok(Whitespace {
value: c.to_string(),
source_info: SourceInfo::new(start.pos, reader.cursor().pos),
})
} else {
Err(ParseError::new(start.pos, true, ParseErrorKind::Space))
}
}
}
}
pub fn one_or_more_spaces(reader: &mut Reader) -> ParseResult<Whitespace> {
let start = reader.cursor();
match one_or_more(space, reader) {
Ok(v) => {
let s = v.iter().map(|x| x.value.clone()).collect();
Ok(Whitespace {
value: s,
source_info: SourceInfo::new(start.pos, reader.cursor().pos),
})
}
Err(e) => Err(e),
}
}
pub fn zero_or_more_spaces(reader: &mut Reader) -> ParseResult<Whitespace> {
let start = reader.cursor();
match zero_or_more(space, reader) {
Ok(v) => {
let s = v.iter().map(|x| x.value.clone()).collect();
Ok(Whitespace {
value: s,
source_info: SourceInfo::new(start.pos, reader.cursor().pos),
})
}
Err(e) => Err(e),
}
}
pub fn line_terminator(reader: &mut Reader) -> ParseResult<LineTerminator> {
let space0 = zero_or_more_spaces(reader)?;
let comment = optional(comment, reader)?;
let nl = if reader.is_eof() {
Whitespace {
value: String::new(),
source_info: SourceInfo::new(reader.cursor().pos, reader.cursor().pos),
}
} else {
match newline(reader) {
Ok(r) => r,
Err(e) => {
let kind = ParseErrorKind::Expecting {
value: String::from("line_terminator"),
};
return Err(ParseError::new(e.pos, false, kind));
}
}
};
Ok(LineTerminator {
space0,
comment,
newline: nl,
})
}
pub fn optional_line_terminators(reader: &mut Reader) -> ParseResult<Vec<LineTerminator>> {
zero_or_more(|p2| recover(line_terminator, p2), reader)
}
pub fn comment(reader: &mut Reader) -> ParseResult<Comment> {
try_literal("#", reader)?;
let start = reader.cursor();
let mut value = String::new();
loop {
if reader.is_eof() {
break;
}
let save_state = reader.cursor();
match newline(reader) {
Ok(_) => {
reader.seek(save_state);
break;
}
_ => {
reader.seek(save_state);
if let Some(c) = reader.read() {
value.push(c);
}
}
}
}
let end = reader.cursor();
Ok(Comment {
value,
source_info: SourceInfo::new(start.pos, end.pos),
})
}
pub fn literal(s: &str, reader: &mut Reader) -> ParseResult<()> {
let start = reader.cursor();
for c in s.chars() {
match reader.read() {
None => {
let kind = ParseErrorKind::Expecting {
value: s.to_string(),
};
return Err(ParseError::new(start.pos, false, kind));
}
Some(x) => {
if x != c {
let kind = ParseErrorKind::Expecting {
value: s.to_string(),
};
return Err(ParseError::new(start.pos, false, kind));
} else {
continue;
}
}
}
}
Ok(())
}
pub fn try_literal(s: &str, reader: &mut Reader) -> ParseResult<()> {
let save_state = reader.cursor();
match literal(s, reader) {
Ok(_) => Ok(()),
Err(e) => {
reader.seek(save_state);
Err(ParseError::new(e.pos, true, e.kind))
}
}
}
pub fn newline(reader: &mut Reader) -> ParseResult<Whitespace> {
let start = reader.cursor();
match try_literal("\r\n", reader) {
Ok(_) => Ok(Whitespace {
value: "\r\n".to_string(),
source_info: SourceInfo::new(start.pos, reader.cursor().pos),
}),
Err(_) => match literal("\n", reader) {
Ok(_) => Ok(Whitespace {
value: "\n".to_string(),
source_info: SourceInfo::new(start.pos, reader.cursor().pos),
}),
Err(_) => {
let kind = ParseErrorKind::Expecting {
value: String::from("newline"),
};
Err(ParseError::new(start.pos, false, kind))
}
},
}
}
pub fn key_value(reader: &mut Reader) -> ParseResult<KeyValue> {
let line_terminators = optional_line_terminators(reader)?;
let space0 = zero_or_more_spaces(reader)?;
let key = recover(key_string::parse, reader)?;
let space1 = zero_or_more_spaces(reader)?;
recover(|reader1| literal(":", reader1), reader)?;
let space2 = zero_or_more_spaces(reader)?;
let value = unquoted_template(reader)?;
let line_terminator0 = line_terminator(reader)?;
Ok(KeyValue {
line_terminators,
space0,
key,
space1,
space2,
value,
line_terminator0,
})
}
pub fn hex(reader: &mut Reader) -> ParseResult<Hex> {
try_literal("hex", reader)?;
literal(",", reader)?;
let space0 = zero_or_more_spaces(reader)?;
let mut value: Vec<u8> = vec![];
let start = reader.cursor();
let mut current: i32 = -1;
loop {
let s = reader.cursor();
match hex_digit(reader) {
Ok(d) => {
if current != -1 {
value.push((current * 16 + d as i32) as u8);
current = -1;
} else {
current = d as i32;
}
}
Err(_) => {
reader.seek(s);
break;
}
};
}
if current != -1 {
return Err(ParseError::new(
reader.cursor().pos,
false,
ParseErrorKind::OddNumberOfHexDigits,
));
}
let source = reader.read_from(start.index).to_source();
let space1 = zero_or_more_spaces(reader)?;
literal(";", reader)?;
Ok(Hex {
space0,
value,
source,
space1,
})
}
pub fn regex(reader: &mut Reader) -> ParseResult<Regex> {
try_literal("/", reader)?;
let start = reader.cursor();
let mut unescaped = String::new();
let mut source = SourceString::new();
source.push('/');
loop {
let c = reader.read();
let Some(c) = c else {
let kind = ParseErrorKind::RegexExpr {
message: "unexpected end of file".to_string(),
};
return Err(ParseError::new(reader.cursor().pos, false, kind));
};
source.push(c);
match c {
'/' => break,
'\\' => {
if let Some('/') = reader.peek() {
reader.read();
source.push('/');
unescaped.push('/');
} else {
unescaped.push('\\');
}
}
c => unescaped.push(c),
}
}
match regex::Regex::new(unescaped.as_str()) {
Ok(inner) => Ok(Regex { inner, source }),
Err(e) => {
let message = match e {
regex::Error::Syntax(s) => {
let lines = s.split('\n').collect::<Vec<&str>>();
let last_line = lines.last().expect("at least one line");
last_line
.strip_prefix("error: ")
.unwrap_or(last_line)
.to_string()
}
regex::Error::CompiledTooBig(_) => "Size limit exceeded".to_string(),
_ => "unknown".to_string(),
};
Err(ParseError::new(
start.pos,
false,
ParseErrorKind::RegexExpr { message },
))
}
}
}
pub fn null(reader: &mut Reader) -> ParseResult<()> {
try_literal("null", reader)
}
pub fn boolean(reader: &mut Reader) -> ParseResult<bool> {
let start = reader.cursor();
match try_literal("true", reader) {
Ok(_) => Ok(true),
Err(_) => match literal("false", reader) {
Ok(_) => Ok(false),
Err(_) => {
let kind = ParseErrorKind::Expecting {
value: String::from("true|false"),
};
Err(ParseError::new(start.pos, true, kind))
}
},
}
}
pub(crate) fn file(reader: &mut Reader) -> ParseResult<File> {
let _start = reader.cursor();
try_literal("file", reader)?;
literal(",", reader)?;
let space0 = zero_or_more_spaces(reader)?;
let f = filename::parse(reader)?;
let space1 = zero_or_more_spaces(reader)?;
literal(";", reader)?;
Ok(File {
space0,
filename: f,
space1,
})
}
pub(crate) fn base64(reader: &mut Reader) -> ParseResult<Base64> {
try_literal("base64", reader)?;
literal(",", reader)?;
let space0 = zero_or_more_spaces(reader)?;
let save_state = reader.cursor();
let value = base64::parse(reader);
let count = reader.cursor().index - save_state.index;
reader.seek(save_state);
let source = reader.read_n(count).to_source();
let space1 = zero_or_more_spaces(reader)?;
literal(";", reader)?;
Ok(Base64 {
space0,
value,
source,
space1,
})
}
pub fn eof(reader: &mut Reader) -> ParseResult<()> {
if reader.is_eof() {
Ok(())
} else {
let kind = ParseErrorKind::Expecting {
value: String::from("eof"),
};
Err(ParseError::new(reader.cursor().pos, false, kind))
}
}
pub fn hex_digit_value(c: char) -> Option<u32> {
match c.to_ascii_lowercase() {
'0' => Some(0),
'1' => Some(1),
'2' => Some(2),
'3' => Some(3),
'4' => Some(4),
'5' => Some(5),
'6' => Some(6),
'7' => Some(7),
'8' => Some(8),
'9' => Some(9),
'a' => Some(10),
'b' => Some(11),
'c' => Some(12),
'd' => Some(13),
'e' => Some(14),
'f' => Some(15),
_ => None,
}
}
pub fn hex_digit(reader: &mut Reader) -> ParseResult<u32> {
let start = reader.cursor();
match reader.read() {
Some(c) => match hex_digit_value(c) {
Some(v) => Ok(v),
None => Err(ParseError::new(start.pos, true, ParseErrorKind::HexDigit)),
},
None => Err(ParseError::new(start.pos, true, ParseErrorKind::HexDigit)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{Expr, ExprKind, Placeholder, Template, TemplateElement, Variable};
use crate::reader::{CharPos, Pos};
use crate::types::ToSource;
#[test]
fn test_space() {
let mut reader = Reader::new("x");
let error = space(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert_eq!(reader.cursor().index, CharPos(1));
let mut reader = Reader::new(" ");
assert_eq!(
space(&mut reader),
Ok(Whitespace {
value: " ".to_string(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 2)),
}),
);
assert_eq!(reader.cursor().index, CharPos(1));
}
#[test]
fn test_one_or_more_spaces() {
let mut reader = Reader::new(" ");
assert_eq!(
one_or_more_spaces(&mut reader),
Ok(Whitespace {
value: " ".to_string(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 3)),
})
);
let mut reader = Reader::new("abc");
let error = one_or_more_spaces(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
}
#[test]
fn test_zero_or_more_spaces() {
let mut reader = Reader::new(" ");
assert_eq!(
zero_or_more_spaces(&mut reader),
Ok(Whitespace {
value: " ".to_string(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 3)),
})
);
assert_eq!(reader.cursor().index, CharPos(2));
let mut reader = Reader::new("xxx");
assert_eq!(
zero_or_more_spaces(&mut reader),
Ok(Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
})
);
assert_eq!(reader.cursor().index, CharPos(0));
let mut reader = Reader::new(" xxx");
assert_eq!(
zero_or_more_spaces(&mut reader),
Ok(Whitespace {
value: " ".to_string(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 2)),
})
);
assert_eq!(reader.cursor().index, CharPos(1));
}
#[test]
fn test_comment() {
let mut reader = Reader::new("#\n");
assert_eq!(
comment(&mut reader),
Ok(Comment {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 2), Pos::new(1, 2)),
})
);
let mut reader = Reader::new("# comment\n");
assert_eq!(
comment(&mut reader),
Ok(Comment {
value: " comment".to_string(),
source_info: SourceInfo::new(Pos::new(1, 2), Pos::new(1, 10)),
})
);
assert_eq!(reader.cursor().index, CharPos(9));
let mut reader = Reader::new("xxx");
let error = comment(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert!(error.recoverable);
}
#[test]
fn test_literal() {
let mut reader = Reader::new("hello");
assert_eq!(literal("hello", &mut reader), Ok(()));
assert_eq!(reader.cursor().index, CharPos(5));
let mut reader = Reader::new("");
let error = literal("hello", &mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert_eq!(
error.kind,
ParseErrorKind::Expecting {
value: String::from("hello")
}
);
assert_eq!(reader.cursor().index, CharPos(0));
let mut reader = Reader::new("hi");
let error = literal("hello", &mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert_eq!(
error.kind,
ParseErrorKind::Expecting {
value: String::from("hello")
}
);
assert_eq!(reader.cursor().index, CharPos(2));
let mut reader = Reader::new("he");
let error = literal("hello", &mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert_eq!(
error.kind,
ParseErrorKind::Expecting {
value: String::from("hello")
}
);
assert_eq!(reader.cursor().index, CharPos(2));
}
#[test]
fn test_new_line() {
let mut reader = Reader::new("\n");
assert_eq!(
newline(&mut reader).unwrap(),
Whitespace {
value: String::from("\n"),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(2, 1)),
}
);
}
#[test]
fn test_key_value() {
let mut reader = Reader::new("message: hello {{name}}! # comment");
assert_eq!(
key_value(&mut reader).unwrap(),
KeyValue {
line_terminators: vec![],
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
},
key: Template::new(
None,
vec![TemplateElement::String {
value: "message".to_string(),
source: "message".to_source(),
}],
SourceInfo::new(Pos::new(1, 1), Pos::new(1, 8)),
),
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(1, 8)),
},
space2: Whitespace {
value: " ".to_string(),
source_info: SourceInfo::new(Pos::new(1, 9), Pos::new(1, 10)),
},
value: Template::new(
None,
vec![
TemplateElement::String {
value: "hello ".to_string(),
source: "hello ".to_source(),
},
TemplateElement::Placeholder(Placeholder {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 18), Pos::new(1, 18)),
},
expr: Expr {
kind: ExprKind::Variable(Variable {
name: "name".to_string(),
source_info: SourceInfo::new(Pos::new(1, 18), Pos::new(1, 22)),
}),
source_info: SourceInfo::new(Pos::new(1, 18), Pos::new(1, 22)),
},
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 22), Pos::new(1, 22)),
},
}),
TemplateElement::String {
value: "!".to_string(),
source: "!".to_source(),
},
],
SourceInfo::new(Pos::new(1, 10), Pos::new(1, 25))
),
line_terminator0: LineTerminator {
space0: Whitespace {
value: " ".to_string(),
source_info: SourceInfo::new(Pos::new(1, 25), Pos::new(1, 26)),
},
comment: Some(Comment {
value: " comment".to_string(),
source_info: SourceInfo::new(Pos::new(1, 27), Pos::new(1, 35)),
}),
newline: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 35), Pos::new(1, 35)),
},
},
}
);
}
#[test]
fn test_key_value_template() {
let mut reader = Reader::new("{{key}}: value");
assert_eq!(
key_value(&mut reader).unwrap(),
KeyValue {
line_terminators: vec![],
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
},
key: Template::new(
None,
vec![TemplateElement::Placeholder(Placeholder {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 3)),
},
expr: Expr {
kind: ExprKind::Variable(Variable {
name: "key".to_string(),
source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 6)),
}),
source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 6))
},
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 6)),
},
})],
SourceInfo::new(Pos::new(1, 1), Pos::new(1, 8)),
),
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(1, 8)),
},
space2: Whitespace {
value: " ".to_string(),
source_info: SourceInfo::new(Pos::new(1, 9), Pos::new(1, 10)),
},
value: Template::new(
None,
vec![TemplateElement::String {
value: "value".to_string(),
source: "value".to_source(),
}],
SourceInfo::new(Pos::new(1, 10), Pos::new(1, 15)),
),
line_terminator0: LineTerminator {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(1, 15)),
},
comment: None,
newline: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(1, 15)),
},
},
}
);
assert_eq!(reader.cursor().index, CharPos(14));
}
#[test]
fn test_key_value_recover() {
let mut reader = Reader::new("{{key");
let error = key_value(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 6 });
assert!(error.recoverable);
assert_eq!(reader.cursor().index, CharPos(5));
let mut reader = Reader::new("GET ®http://google.fr");
let error = key_value(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 5 });
assert!(error.recoverable);
assert_eq!(reader.cursor().index, CharPos(5)); }
#[test]
fn test_boolean() {
let mut reader = Reader::new("true");
assert!(boolean(&mut reader).unwrap());
let mut reader = Reader::new("xxx");
let error = boolean(&mut reader).err().unwrap();
assert_eq!(
error.kind,
ParseErrorKind::Expecting {
value: String::from("true|false")
}
);
assert!(error.recoverable);
let mut reader = Reader::new("trux");
let error = boolean(&mut reader).err().unwrap();
assert_eq!(
error.kind,
ParseErrorKind::Expecting {
value: String::from("true|false")
}
);
assert!(error.recoverable);
}
#[test]
fn test_hex_digit() {
let mut reader = Reader::new("0");
assert_eq!(hex_digit(&mut reader).unwrap(), 0);
let mut reader = Reader::new("x");
let error = hex_digit(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert_eq!(error.kind, ParseErrorKind::HexDigit);
assert!(error.recoverable);
}
#[test]
fn test_hex() {
let mut reader = Reader::new("hex, ff;");
assert_eq!(
hex(&mut reader).unwrap(),
Hex {
space0: Whitespace {
value: " ".to_string(),
source_info: SourceInfo::new(Pos::new(1, 5), Pos::new(1, 6)),
},
value: vec![255],
source: "ff".to_source(),
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(1, 8)),
},
}
);
let mut reader = Reader::new("hex,010203 ;");
assert_eq!(
hex(&mut reader).unwrap(),
Hex {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 5), Pos::new(1, 5)),
},
value: vec![1, 2, 3],
source: "010203".to_source(),
space1: Whitespace {
value: " ".to_string(),
source_info: SourceInfo::new(Pos::new(1, 11), Pos::new(1, 12)),
},
}
);
}
#[test]
fn test_hex_error() {
let mut reader = Reader::new("hex,012;");
let error = hex(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 8 });
assert_eq!(error.kind, ParseErrorKind::OddNumberOfHexDigits);
}
#[test]
fn test_regex() {
let mut reader = Reader::new(r#"/a{3}/"#);
assert_eq!(
regex(&mut reader).unwrap(),
Regex {
inner: regex::Regex::new(r#"a{3}"#).unwrap(),
source: r#"/a{3}/"#.to_source(),
}
);
let mut reader = Reader::new(r"/a\/b/");
assert_eq!(
regex(&mut reader).unwrap(),
Regex {
inner: regex::Regex::new(r#"a/b"#).unwrap(),
source: r"/a\/b/".to_source(),
}
);
let mut reader = Reader::new(r"/a\.b/");
assert_eq!(
regex(&mut reader).unwrap(),
Regex {
inner: regex::Regex::new(r"a\.b").unwrap(),
source: r"/a\.b/".to_source(),
}
);
let mut reader = Reader::new(r"/\d{4}-\d{2}-\d{2}/");
assert_eq!(
regex(&mut reader).unwrap(),
Regex {
inner: regex::Regex::new(r"\d{4}-\d{2}-\d{2}").unwrap(),
source: r"/\d{4}-\d{2}-\d{2}/".to_source(),
}
);
}
#[test]
fn test_regex_error() {
let mut reader = Reader::new("xxx");
let error = regex(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert!(error.recoverable);
let mut reader = Reader::new("/xxx");
let error = regex(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 5 });
assert!(!error.recoverable);
assert_eq!(
error.kind,
ParseErrorKind::RegexExpr {
message: "unexpected end of file".to_string()
}
);
let mut reader = Reader::new("/x{a}/");
let error = regex(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 2 });
assert!(!error.recoverable);
assert_eq!(
error.kind,
ParseErrorKind::RegexExpr {
message: "repetition quantifier expects a valid decimal".to_string()
}
);
}
#[test]
fn test_file() {
let mut reader = Reader::new("file,data.xml;");
assert_eq!(
file(&mut reader).unwrap(),
File {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 6)),
},
filename: Template::new(
None,
vec![TemplateElement::String {
value: "data.xml".to_string(),
source: "data.xml".to_source(),
}],
SourceInfo::new(Pos::new(1, 6), Pos::new(1, 14))
),
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 14), Pos::new(1, 14)),
},
}
);
let mut reader = Reader::new("file, filename1;");
assert_eq!(
file(&mut reader).unwrap(),
File {
space0: Whitespace {
value: String::from(" "),
source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 7)),
},
filename: Template::new(
None,
vec![TemplateElement::String {
value: "filename1".to_string(),
source: "filename1".to_source(),
}],
SourceInfo::new(Pos::new(1, 7), Pos::new(1, 16)),
),
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 16), Pos::new(1, 16)),
},
}
);
let mut reader = Reader::new("file, tmp/filename1;");
assert_eq!(
file(&mut reader).unwrap(),
File {
space0: Whitespace {
value: String::from(" "),
source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 7)),
},
filename: Template::new(
None,
vec![TemplateElement::String {
value: "tmp/filename1".to_string(),
source: "tmp/filename1".to_source(),
}],
SourceInfo::new(Pos::new(1, 7), Pos::new(1, 20))
),
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 20), Pos::new(1, 20)),
},
}
);
let mut reader = Reader::new(r"file, tmp/filename\ with\ spaces.txt;");
assert_eq!(
file(&mut reader).unwrap(),
File {
space0: Whitespace {
value: String::from(" "),
source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 7)),
},
filename: Template::new(
None,
vec![TemplateElement::String {
value: "tmp/filename with spaces.txt".to_string(),
source: "tmp/filename\\ with\\ spaces.txt".to_source(),
}],
SourceInfo::new(Pos::new(1, 7), Pos::new(1, 37))
),
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 37), Pos::new(1, 37)),
},
}
);
}
#[test]
fn test_file_error() {
let mut reader = Reader::new("fil; filename1;");
let error = file(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert!(error.recoverable);
let mut reader = Reader::new("file, filename1");
let error = file(&mut reader).err().unwrap();
assert_eq!(
error.pos,
Pos {
line: 1,
column: 16,
}
);
assert!(!error.recoverable);
assert_eq!(
error.kind,
ParseErrorKind::Expecting {
value: String::from(";")
}
);
let mut reader = Reader::new(r"file, tmp/filename\ with\ unescaped .txt;");
let error = file(&mut reader).err().unwrap();
assert_eq!(
error.pos,
Pos {
line: 1,
column: 37,
}
);
assert!(!error.recoverable);
assert_eq!(
error.kind,
ParseErrorKind::Expecting {
value: String::from(";")
}
);
}
#[test]
fn test_base64() {
let mut reader = Reader::new("base64, T WE=;xxx");
assert_eq!(
base64(&mut reader).unwrap(),
Base64 {
space0: Whitespace {
value: String::from(" "),
source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(1, 10)),
},
value: vec![77, 97],
source: "T WE=".to_source(),
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(1, 15)),
},
}
);
assert_eq!(reader.cursor().index, CharPos(15));
}
}