1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! This module contains functions to generate a [`Vec`] of [`Value`] from a given input
//!
//! **The functions and structs here should _not_ be considered stable**

use crate::utils::NotationMatching;

mod preprocessor;

/// The enum used to represent the distinct _raw_ values of a comment
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Value {
    /// The first [`String`] is the _notation_ found, and the second [`String`] are the _contents without the notation_
    Notation(String, String),
    /// Raw text, without notation
    Text(String),
    /// Double new-line, or any other separator
    Separator,
    /// Unknown value
    Unknown,
}

fn parse_single_line(line: &str) -> Value {
    let line = preprocessor::preprocess_line(line);
    if let Some(notation) = line.contains_any_notation() {
        let split = line.split_whitespace().collect::<Vec<&str>>();
        Value::Notation(notation, split[1..].to_vec().join(" "))
    } else if line.is_empty() {
        Value::Separator
    } else {
        Value::Text(line)
    }
}

/// Generate a [`Vec`] of [`Value`] from a given [`&str`]
///
/// # Examples
/// ```
/// use doxygen_rs::parser::parse_comment;
///
/// let parsed = parse_comment("@brief Random function");
/// ```
pub fn parse_comment(input: &str) -> Vec<Value> {
    let lines = input.split('\n').map(|v| v.to_string()).collect::<Vec<String>>();
    let mut values = vec![];

    for line in lines {
        let value = if line.trim().starts_with("* ") {
            parse_single_line(line.replace("* ", "").as_str())
        } else {
            parse_single_line(line.as_str())
        };

        values.push(value);
    }
    values.push(Value::Separator);

    values
}

/// The enum used to represent values of a _raw_ bindgen file
#[derive(Clone, Debug)]
pub enum StringType {
    /// Parsed value
    Parsed(Vec<Value>),
    /// No-doc, value is given raw
    Raw(String),
}

/// Generate a [`Vec`] of [`StringType`] from a given [`&str`], assuming it's a _raw_ bindgen file
pub fn parse_bindgen(input: &str) -> Vec<StringType> {
    let lines: Vec<String> = input.split('\n').map(|v| v.to_string()).collect::<Vec<String>>();
    let mut strings = vec![];

    let mut comment_buffer = vec![];
    for line in lines {
        if line.trim().starts_with("#[doc = \"") && line.trim().ends_with("\"]") {
            comment_buffer.push(line.replace("#[doc = \"", "").replace("\"]", ""));
        } else {
            if !comment_buffer.is_empty() {
                strings.push(StringType::Parsed(parse_comment(comment_buffer.join("\n").as_str())));
                comment_buffer = vec![];
            }
            strings.push(StringType::Raw(line));
        }
    }

    strings
}

#[cfg(test)]
mod tests {
    use crate::parser::parse_comment;
    use crate::parser::Value::Notation;

    #[test]
    fn test() {
        let parsed = parse_comment("@param random Random thing lmao\n\n@block This is going to be\nA block of text\nThis is crazy right??\n\nHello this is not anotated\n");
        println!("{:?}", parsed);
    }

    #[test]
    fn italic_works() {
        let parsed = parse_comment("@brief \\a example \\\\e example 2 @em example 3");
        assert_eq!(parsed[0], Notation("@brief".to_owned(), "*example* *example* 2 *example* 3".to_owned()))
    }

    #[test]
    fn emojis_work() {
        let parsed = parse_comment("@brief @emoji :smirk: \\emoji smirk \\\\emoji smiley");
        assert_eq!(parsed[0], Notation("@brief".to_owned(), "😏 😏 😃".to_owned()))
    }
}