comrak 0.1.0

A 100% CommonMark-compatible GitHub Flavored Markdown parser and formatter
Documentation
use ctype::{ispunct, isspace};
use entity;
use inlines::AutolinkType;

pub fn unescape(v: &mut String) {
    let mut r = 0;
    let mut sz = v.len();

    while r < sz {
        if v.as_bytes()[r] == '\\' as u8 && r + 1 < sz && ispunct(&v.as_bytes()[r + 1]) {
            v.remove(r);
            sz -= 1;
        }
        if r >= sz {
            break;
        }
        r += 1;
    }
}

pub fn clean_autolink(url: &str, kind: AutolinkType) -> String {
    let mut url_string = url.to_string();
    trim(&mut url_string);

    if url_string.len() == 0 {
        return url_string;
    }

    let mut buf = String::new();
    if kind == AutolinkType::Email {
        buf += "mailto:";
    }

    buf += &entity::unescape_html(&url_string);
    buf
}

pub fn normalize_whitespace(v: &str) -> String {
    let mut last_char_was_space = false;
    let mut r = String::new();

    for c in v.chars() {
        if (c as u32) < 0x80 && isspace(&(c as u8)) {
            if !last_char_was_space {
                r.push(' ');
                last_char_was_space = true;
            }
        } else {
            r.push(c);
            last_char_was_space = false;
        }
    }

    r
}

pub fn remove_trailing_blank_lines(line: &mut String) {
    let mut i = line.len() - 1;
    loop {
        let c = line.as_bytes()[i];

        if c != ' ' as u8 && c != '\t' as u8 && !is_line_end_char(&c) {
            break;
        }

        if i == 0 {
            line.clear();
            return;
        }

        i -= 1;
    }

    for i in i..line.len() {
        let c = line.as_bytes()[i];

        if !is_line_end_char(&c) {
            continue;
        }

        line.truncate(i);
        break;
    }
}

pub fn is_line_end_char(ch: &u8) -> bool {
    match ch {
        &10 | &13 => true,
        _ => false,
    }
}

pub fn is_space_or_tab(ch: &u8) -> bool {
    match ch {
        &9 | &32 => true,
        _ => false,
    }
}

pub fn chop_trailing_hashtags(line: &mut String) {
    rtrim(line);

    let orig_n = line.len() - 1;
    let mut n = orig_n;

    while line.as_bytes()[n] == '#' as u8 {
        if n == 0 {
            return;
        }
        n -= 1;
    }

    if n != orig_n && is_space_or_tab(&line.as_bytes()[n]) {
        line.truncate(n);
        rtrim(line);
    }
}

pub fn rtrim(line: &mut String) {
    let mut len = line.len();
    while len > 0 && isspace(&line.as_bytes()[len - 1]) {
        line.pop();
        len -= 1;
    }
}

pub fn ltrim(line: &mut String) {
    let mut len = line.len();
    while len > 0 && isspace(&line.as_bytes()[0]) {
        line.remove(0);
        len -= 1;
    }
}

pub fn trim(line: &mut String) {
    ltrim(line);
    rtrim(line);
}

pub fn trim_slice(mut i: &str) -> &str {
    let mut len = i.len();
    while len > 0 && isspace(&i.as_bytes()[0]) {
        i = &i[1..];
        len -= 1;
    }
    while len > 0 && isspace(&i.as_bytes()[len - 1]) {
        i = &i[..len - 1];
        len -= 1;
    }
    i
}

pub fn clean_url(url: &str) -> String {
    let url = trim_slice(url);

    let url_len = url.len();
    if url_len == 0 {
        return String::new();
    }

    let mut b = if url.as_bytes()[0] == '<' as u8 && url.as_bytes()[url_len - 1] == '>' as u8 {
        entity::unescape_html(&url[1..url_len - 1])
    } else {
        entity::unescape_html(url)
    };

    unescape(&mut b);
    b
}

pub fn clean_title(title: &str) -> String {
    let title_len = title.len();
    if title_len == 0 {
        return String::new();
    }

    let first = title.as_bytes()[0];
    let last = title.as_bytes()[title_len - 1];

    let mut b = if (first == '\'' as u8 && last == '\'' as u8) ||
                   (first == '(' as u8 && last == ')' as u8) ||
                   (first == '"' as u8 && last == '"' as u8) {
        entity::unescape_html(&title[1..title_len - 1])
    } else {
        entity::unescape_html(title)
    };

    unescape(&mut b);
    b
}

pub fn is_blank(s: &str) -> bool {
    for c in s.as_bytes() {
        match *c {
            10 | 13 => return true,
            32 | 9 => (),
            _ => return false,
        }
    }
    true
}

pub fn normalize_reference_label(i: &str) -> String {
    let i = trim_slice(i);
    let mut v = String::new();
    let mut last_was_whitespace = false;
    for c in i.chars() {
        for e in c.to_lowercase() {
            if e.is_whitespace() {
                if !last_was_whitespace {
                    last_was_whitespace = true;
                    v.push(' ');
                }
            } else {
                last_was_whitespace = false;
                v.push(e);
            }
        }
    }
    v
}