mod sections;
mod styles;
#[macro_use]
mod macros;
use super::Stream;
use crate::{
ast::{Content, FmlValue, FmlValues, Section},
parser::{
State,
functions::{
sections::split_sections,
styles::{code_block, fml_values},
},
},
};
use std::io::{self, ErrorKind};
use winnow::{
Result,
ascii::*,
combinator::*,
error::{ParserError, StrContext},
prelude::*,
token::*,
};
pub fn parse(input: &str) -> io::Result<Content> {
let err = |_| io::Error::from(ErrorKind::InvalidInput);
let section_scopes = split_sections(input).map_err(err.clone())?;
let mut sections: Vec<Section> = vec![];
for scope in section_scopes {
sections.push(Section(
fml_values(Stream {
input: scope,
..Default::default()
})
.map_err(err)?,
));
}
Ok(Content(sections))
}
pub fn parser() {}
#[cfg(test)]
mod tests {
use super::*;
fn str2stream<'i>(input: &'i str) -> Stream<'i> {
Stream {
input,
state: State::default(),
}
}
#[cfg(test)]
mod delimited {
use super::*;
#[test]
fn normal() {
let mut inp = r"*styled text*";
let res: Result<String> = delimited_esc!("*").parse_next(&mut inp);
let exp = "styled text".to_string();
assert_eq!(res.unwrap(), exp);
}
#[test]
fn escaped() {
let mut inp = r"*styled \* line \of t\\ext*";
let res: Result<String> = delimited_esc!("*").parse_next(&mut inp);
let exp = r"styled * line \of t\ext".to_string();
assert_eq!(res.unwrap(), exp);
}
#[test]
fn unescaped() {
let mut inp = r"*styled text\\*";
let res: Result<String> = delimited_esc!("*").parse_next(&mut inp);
let exp = r"styled text\".to_string();
assert_eq!(res.unwrap(), exp);
}
#[cfg(test)]
mod fails {
use super::*;
#[test]
fn empty() {
let mut inp = r"**";
let res: Result<String> = delimited_esc!("*").parse_next(&mut inp);
assert!(res.is_err());
}
#[test]
fn blank() {
let mut inp = r"* *";
let res: Result<String> = delimited_esc!("*").parse_next(&mut inp);
assert!(res.is_err());
}
#[test]
fn blank_multiline() {
let mut inp = r"*
*";
let res: Result<String> = delimited_esc!("*").parse_next(&mut inp);
assert!(res.is_err());
}
#[test]
fn escape_start() {
let mut inp = r"\*styled text*";
let res: Result<String> = delimited_esc!("*").parse_next(&mut inp);
assert!(res.is_err());
}
#[test]
fn escape_end() {
let mut inp = r"*styled text\*";
let res: Result<String> = delimited_esc!("*").parse_next(&mut inp);
assert!(res.is_err());
}
}
}
#[cfg(test)]
mod code_block {
use super::*;
#[test]
fn one_line() {
let inp = r"```javax.util.AbstractShortestJavaClassPath.MandatoryGlobalState.doSomething()```";
let res = code_block.parse(str2stream(inp));
let exp = FmlValue::CodeBlock {
lang: None,
body:
r"javax.util.AbstractShortestJavaClassPath.MandatoryGlobalState.doSomething()"
.to_string(),
};
assert_eq!(res.unwrap(), exp);
}
#[test]
fn one_line_lang() {
let inp = r"```java javax.util.AbstractShortestJavaClassPath.MandatoryGlobalState.doSomething()```";
let res = code_block.parse(str2stream(inp));
let exp = FmlValue::CodeBlock {
lang: Some("java".to_string()),
body:
r"javax.util.AbstractShortestJavaClassPath.MandatoryGlobalState.doSomething()"
.to_string(),
};
assert_eq!(res.unwrap(), exp);
}
#[test]
fn multiline() {
let inp = r#"```
fn main() {
println!("Hello, world!");
}
```"#;
let res = code_block.parse(str2stream(inp));
let exp = FmlValue::CodeBlock {
lang: None,
body: r#"fn main() {
println!("Hello, world!");
}"#
.to_string(),
};
assert_eq!(res.unwrap(), exp);
}
#[test]
fn multiline_lang() {
let inp = r#"```rust
fn main() {
println!("Hello, world!");
}
```"#;
let res = code_block.parse(str2stream(inp));
let exp = FmlValue::CodeBlock {
lang: Some("rust".to_string()),
body: r#"fn main() {
println!("Hello, world!");
}"#
.to_string(),
};
assert_eq!(res.unwrap(), exp);
}
mod fails {
use super::*;
#[test]
fn empty() {
let inp = "``````";
let res = code_block.parse(str2stream(inp));
assert!(res.is_err());
}
#[test]
fn blank() {
let inp = "``` ```";
let res = code_block.parse(str2stream(inp));
eprintln!("{res:#?}");
assert!(res.is_err());
}
#[test]
fn blank_multiline() {
let inp = r"```
```";
let res = code_block.parse(str2stream(inp));
eprintln!("{res:#?}");
assert!(res.is_err());
}
}
}
}