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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
use std::{fs, path::PathBuf};

use anyhow::Result;
use glob::glob;

use crate::{
    codegen::{gen::Generator, java::gen_java_code},
    equals_throw,
    generate::gen_code,
    parser::parser::classes,
};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BindgenConfig {
    /// The Java package to generate bindings for.
    pub package: String,

    /// The input `.rs4j` files (or globs) that get processed.
    pub files: Vec<PathBuf>,

    /// The directory where Java bindings.
    pub output: PathBuf,

    /// The path of the file to generate Rust bindings in.
    pub bindings: PathBuf,

    /// Enable using JetBrains annotations?
    pub annotations: bool,
}

impl BindgenConfig {
    pub fn new() -> BindgenConfig {
        BindgenConfig {
            package: String::new(),
            files: Vec::new(),
            output: PathBuf::new(),
            bindings: PathBuf::new(),
            annotations: false,
        }
    }

    pub fn output<T>(&mut self, val: T) -> Self
    where
        T: Into<PathBuf>,
    {
        self.output = val.into();
        self.clone()
    }

    pub fn bindings<T>(&mut self, val: T) -> Self
    where
        T: Into<PathBuf>,
    {
        self.bindings = val.into();
        self.clone()
    }

    pub fn package<T>(&mut self, val: T) -> Self
    where
        T: AsRef<str>,
    {
        self.package = val.as_ref().to_string();
        self.clone()
    }

    pub fn pkg<T>(&mut self, val: T) -> Self
    where
        T: AsRef<str>,
    {
        self.package(val)
    }

    pub fn file<T>(&mut self, val: T) -> Self
    where
        T: Into<PathBuf>,
    {
        self.files.push(val.into());
        self.clone()
    }

    pub fn files<T>(&mut self, val: Vec<T>) -> Self
    where
        T: Into<PathBuf>,
    {
        for file in val {
            self.files.push(file.into());
        }

        self.clone()
    }

    pub fn glob<T>(&mut self, val: T) -> Result<Self>
    where
        T: AsRef<str>,
    {
        for file in glob(val.as_ref())? {
            if let Ok(file) = file {
                self.files.push(file);
            }
        }

        Ok(self.clone())
    }

    pub fn annotations(&mut self, val: bool) -> Self {
        self.annotations = val;
        self.clone()
    }

    pub fn generate(&self) -> Result<()> {
        equals_throw!(
            self.files,
            Vec::<PathBuf>::new(),
            "Files array cannot be empty!"
        );
        equals_throw!(self.package, "", "Package cannot be blank!");
        equals_throw!(
            self.output,
            PathBuf::new(),
            "Output directory must be specified!"
        );
        equals_throw!(
            self.bindings,
            PathBuf::new(),
            "Output file must be specified!"
        );

        self.generate_bindings()
    }

    fn generate_bindings(&self) -> Result<()> {
        let gen = Generator {
            package: self.package.clone(),
            with_annotations: self.annotations,
        };

        let mut exprs = Vec::new();

        for file in self.files.clone() {
            let data = fs::read_to_string(file)?;

            exprs.append(&mut classes(data.as_str())?);
        }

        gen_code(gen.clone(), exprs.clone(), self.bindings.clone())?;
        gen_java_code(gen, exprs, self.output.clone())?;

        Ok(())
    }
}