doubter-impl 0.1.0

Internal implementation of doubter
Documentation
use bytecount;
use pulldown_cmark as cmark;
use std::borrow::Cow;

#[derive(Debug, PartialEq)]
pub struct CodeBlock<'a> {
    pub line: usize,
    pub info: Cow<'a, str>,
    pub content: Vec<Cow<'a, str>>,
}

pub fn extract_code_blocks(input: &str) -> Vec<CodeBlock> {
    let mut parser = cmark::Parser::new(input);

    let mut blocks = vec![];
    let mut block = None;

    while let Some(event) = parser.next() {
        match event {
            cmark::Event::Start(cmark::Tag::CodeBlock(info)) => {
                assert!(block.is_none());

                let offset = parser.get_offset();
                let line = bytecount::count(&input.as_bytes()[..offset], b'\n');

                block = Some(CodeBlock {
                    line,
                    info,
                    content: vec![],
                });
            }
            cmark::Event::Text(line) => {
                if let Some(ref mut block) = block {
                    block.content.push(line);
                }
            }
            cmark::Event::End(cmark::Tag::CodeBlock(info)) => {
                let block = block.take().expect("unexpected condition");
                assert_eq!(block.info, info);
                blocks.push(block);
            }
            _ => {}
        }
    }

    blocks
}

#[cfg(test)]
mod tests {
    use super::{extract_code_blocks, CodeBlock};

    #[test]
    fn test_no_blocks() {
        const INPUT: &str = "\
aaa
bbb
ccc";
        assert_eq!(extract_code_blocks(INPUT), vec![]);
    }

    #[test]
    fn test_single_block() {
        const INPUT: &str = "\
header.

```rust
fn main() {}
```

footer.";
        assert_eq!(
            extract_code_blocks(INPUT),
            vec![CodeBlock {
                line: 3,
                info: "rust".into(),
                content: vec!["fn main() {}\n".into(),],
            }]
        );
    }
}