fml 0.6.8

Friendly Markup Language
Documentation
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))
}

/// Deprecated
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());
            }
        }

        // KUUUUUUUUURRWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
        // note to self: repeat_till applies middle parser BEFORE checking with the third
        // #[test]
        // fn fuck() {
        // 	use winnow::combinator::repeat_till;

        // 	fn parser<'i>(s: &mut &'i str) -> Result<String> {
        // 		repeat_till(1.., any, "e").map(|(s, _)| s).parse_next(s)
        // 	}

        //	assert!(parser.parse_peek("e").is_err());
        //  assert!(parser.parse_peek("ae").is_ok());
        // 	assert!(parser.parse("ee").is_err()); // FAILS!!!
        // }
    }
}