cargo-insert-docs 1.6.0

Inserts feature docs into crate docs, and crate docs into README.
pub mod lang_string;
mod section;
#[cfg(test)]
mod tests;
mod tree;

use std::borrow::Cow;

use percent_encoding::percent_encode_byte;

use crate::{markdown_rs::event::Name, string_replacer::StringReplacer};

pub use section::{find_section, find_subsections};
pub use tree::Tree;

pub fn extract_definitions(markdown: &str) -> [String; 2] {
    let mut out = StringReplacer::new(markdown);
    let mut definitions: Vec<&str> = vec![];
    let tree = Tree::new(markdown);

    for node in tree.depth_first() {
        if node.name() == Name::Definition {
            let mut range = node.byte_range();
            range.end = end_of_line(markdown, range.end);
            let value = &markdown[range.clone()];
            definitions.push(value);
            out.remove(range);
        }
    }

    let without_definitions = out.finish();
    let definitions = definitions.join("");

    [without_definitions, definitions]
}

pub fn end_of_line(markdown: &str, index: usize) -> usize {
    match markdown[index..].bytes().position(|b| b == b'\n') {
        Some(i) => index + i + 1,
        None => markdown.len(),
    }
}

pub fn format_link_destination(destination: &str) -> Cow<'_, str> {
    let needs_angle_brackets = destination.is_empty()
        || destination.starts_with('<')
        || destination
            .chars()
            .any(|c| c.is_ascii_whitespace() || c.is_ascii_control() || c == '(' || c == ')');

    if !needs_angle_brackets {
        return Cow::Borrowed(destination);
    }

    let mut out = String::new();
    out.push('<');

    for char in destination.chars() {
        if matches!(char, '\n' | '<' | '>') {
            out.push_str(percent_encode_byte(char as u8));
        } else {
            out.push(char);
        }
    }

    out.push('>');
    Cow::Owned(out)
}