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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
mod parser;

pub use parser::ParseError;

use std::{
    fs::{read_dir, read_to_string},
    path::Path,
};

use parser::parse_module;

pub fn load_api_from_dir<P: AsRef<Path>>(path: P) -> Result<Api, ParseError> {
    let mut api = Api {
        modules: Vec::new(),
    };

    for path in read_dir(path).unwrap() {
        let path = path.unwrap().path();

        let module_name = path.file_stem().unwrap().to_str().unwrap();

        let module_str = read_to_string(&path).unwrap();
        let module = load_module_from_str(module_name, &module_str)?;

        api.modules.push(module);
    }

    Ok(api)
}

pub fn load_module_from_str<S: ToString>(name: S, cog: &str) -> Result<Module, ParseError> {
    parse_module(name.to_string(), cog)
}

#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};

#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Api {
    pub modules: Vec<Module>,
}

#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Module {
    pub name: String,
    pub documentation: Option<String>,
    pub interfaces: Vec<Interface>,
}

#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Interface {
    pub name: String,
    pub documentation: Option<String>,
    pub functions: Vec<Function>,
}

#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Function {
    pub name: String,
    pub documentation: Option<String>,
    pub parameters: Vec<Parameter>,
    pub return_type: Option<Type>,
}

#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Parameter {
    pub name: String,
    pub type_: Type,
}

#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Type {
    pub modifier: TypeModifier,
    pub type_name: TypeName,
}

#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TypeModifier {
    None,
    Pointer,
    Reference,
}

#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TypeName {
    CString,
    Any,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn single_interface() {
        let code = "interface foo { fn bar(); }";
        let result = load_module_from_str("test", &code);

        assert!(result.is_ok());

        let module = result.unwrap();

        assert_eq!(module.interfaces.len(), 1);

        let interface = &module.interfaces[0];
        assert_eq!(interface.name, "foo");
        assert_eq!(interface.functions.len(), 1);

        let function = &interface.functions[0];
        assert_eq!(function.name, "bar");
    }

    #[test]
    fn comment_at_end_of_file() {
        let code = "// comment";
        let result = load_module_from_str("test", &code);

        assert!(result.is_ok());
    }
}