1use std::collections::HashSet;
8use std::error::Error;
9use std::fs;
10use std::mem::take;
11use std::path::{Path, PathBuf};
12
13mod const_expr;
14mod generator;
15mod parser;
16mod type_generator;
17pub use generator::Generator;
18pub use parser::parse_document;
19
20#[derive(Default, Hash, Eq, PartialEq, Debug, Clone)]
21pub struct Namespace {
22 ns: Vec<String>,
23}
24
25impl Namespace {
26 pub const AIDL: &'static str = ".";
27 pub const RUST: &'static str = "::";
28
29 pub fn new(namespace: &str, style: &str) -> Self {
30 Self {
31 ns: namespace.split(style).map(|s| s.into()).collect(),
32 }
33 }
34
35 pub fn push(&mut self, name: &str) {
36 self.ns.push(name.into())
37 }
38
39 pub fn push_ns(&mut self, ns: &Namespace) {
40 self.ns.extend_from_slice(&ns.ns);
41 }
42
43 pub fn pop(&mut self) -> Option<String> {
44 self.ns.pop()
45 }
46
47 pub fn to_string(&self, style: &str) -> String {
48 self.ns.join(style)
49 }
50
51 pub fn relative_mod(&self, target: &Namespace) -> String {
52 let mut curr_ns = self.ns.clone();
53 let mut target_ns = target.ns.clone();
54
55 let mut index_to_remove = 0;
56
57 for (item1, item2) in curr_ns.iter().zip(target_ns.iter()) {
58 if item1 == item2 {
59 index_to_remove += 1;
60 } else {
61 break;
62 }
63 }
64
65 curr_ns.drain(0..index_to_remove);
66 target_ns.drain(0..index_to_remove);
67
68 "super::".repeat(curr_ns.len()) + &target_ns.join(Self::RUST)
69 }
70}
71
72pub fn indent_space(step: usize) -> String {
73 let indent = " ";
74 let mut ret = String::new();
75
76 for _ in 0..step {
77 ret += indent;
78 }
79
80 ret
81}
82
83pub fn add_indent(step: usize, source: &str) -> String {
84 let mut content = String::new();
85 for line in source.lines() {
86 if !line.is_empty() {
87 content += &(indent_space(step) + line + "\n");
88 } else {
89 content += "\n";
90 }
91 }
92 content
93}
94
95pub struct Builder {
96 sources: Vec<PathBuf>,
97 includes: Vec<PathBuf>,
98 dest_dir: PathBuf,
99 output: PathBuf,
100 enabled_async: bool,
101 is_crate: bool,
102}
103
104impl Default for Builder {
105 fn default() -> Self {
106 Self::new()
107 }
108}
109
110impl Builder {
111 pub fn new() -> Self {
112 parser::reset();
113 Self {
114 sources: Vec::new(),
115 includes: Vec::new(),
116 dest_dir: PathBuf::from(std::env::var_os("OUT_DIR").unwrap_or("aidl_gen".into())),
117 output: "rsbinder_generated_aidl.rs".into(),
118 enabled_async: cfg!(feature = "async"),
119 is_crate: false,
120 }
121 }
122
123 pub fn source(mut self, source: impl AsRef<Path>) -> Self {
124 self.sources.push(source.as_ref().into());
125 self
126 }
127
128 pub fn include_dir(mut self, dir: impl AsRef<Path>) -> Self {
129 self.includes.push(dir.as_ref().into());
130 self
131 }
132
133 pub fn output(mut self, output: impl AsRef<Path>) -> Self {
134 let mut output = output.as_ref().to_owned();
135
136 if output.extension().is_none() {
137 output.set_extension("rs");
138 }
139
140 self.output = output;
141
142 self
143 }
144
145 pub fn set_async_support(mut self, enable: bool) -> Self {
146 self.enabled_async = enable;
147 self
148 }
149
150 pub fn set_crate_support(mut self, enable: bool) -> Self {
153 self.is_crate = enable;
154 type_generator::set_crate_support(enable);
155 self
156 }
157
158 fn parse_file(filename: &Path) -> Result<(String, parser::Document), Box<dyn Error>> {
159 println!("Parsing: {filename:?}");
160 let unparsed_file = fs::read_to_string(filename)?;
161 let document = parser::parse_document(&unparsed_file)?;
162
163 Ok((
164 filename.file_stem().unwrap().to_str().unwrap().to_string(),
165 document,
166 ))
167 }
168
169 fn generate_all(
170 &self,
171 mut package_list: Vec<(String, String, String)>,
172 ) -> Result<String, Box<dyn Error>> {
173 let mut content = String::new();
174 let mut namespace = String::new();
175 let mut mod_count: usize = 0;
176
177 content += "#[allow(clippy::all)]\n";
178 content += "#[allow(unused_imports)]\n\n";
179
180 package_list.sort();
181
182 for package in package_list {
183 if namespace != package.0 {
184 let namespace_split: Vec<&str> = namespace.split('.').collect();
185 let mod_list: Vec<&str> = package.0.split('.').collect();
186
187 let cmp_len = std::cmp::min(namespace_split.len(), mod_list.len());
188 let mut start = 0;
189
190 for i in 0..cmp_len {
191 if namespace_split[i] == mod_list[i] {
192 start += 1;
193 } else {
194 break;
195 }
196 }
197
198 for i in (start..mod_count).rev() {
199 content += &indent_space(i);
200 content += "}\n";
201 }
202
203 namespace = package.0.clone();
204 mod_count = start;
205
206 for r#mod in &mod_list[start..] {
207 content += &indent_space(mod_count);
208 content += &format!("pub mod {mod} {{\n");
209 mod_count += 1;
210 }
211 }
212
213 content += &add_indent(mod_count, &package.1);
214 }
215
216 for i in (0..mod_count).rev() {
217 content += &indent_space(i);
218 content += "}\n";
219 }
220
221 Ok(content)
222 }
223
224 fn parse_sources(&mut self) -> Result<Vec<(String, parser::Document)>, Box<dyn Error>> {
225 let mut sources = take(&mut self.sources);
226 let mut seen = HashSet::new();
227 let mut includes = take(&mut self.includes).into_iter().collect::<HashSet<_>>();
228 let mut document_list = Vec::new();
229
230 fn strip_package(path: &Path, package: &str) -> Option<PathBuf> {
231 let mut components = path.components();
232 for package in package.split('.').rev() {
233 if components.next_back()?.as_os_str().to_str()? != package {
234 return None;
235 }
236 }
237 Some(components.collect())
238 }
239
240 while !sources.is_empty() {
241 for path in take(&mut sources) {
242 if seen.contains(&path) {
243 continue;
244 }
245
246 if path.is_file() {
247 let (name, doc) = Self::parse_file(&path)?;
248
249 if let Some(dir) = doc
250 .package
251 .as_ref()
252 .and_then(|p| strip_package(path.parent()?, p))
253 {
254 includes.insert(dir);
255 }
256
257 for import in doc.imports.values() {
258 let rel_path =
259 PathBuf::from(import.replace('.', "/")).with_extension("aidl");
260 let mut found = false;
261 for include_dir in &includes {
262 let path = include_dir.join(&rel_path);
263 if path.exists() {
264 sources.push(path);
265 found = true;
266 break;
267 }
268 }
269
270 if !found {
271 return Err(format!(
272 "import {import} not found, imported from {path:?}"
273 )
274 .into());
275 }
276 }
277
278 document_list.push((name, doc));
279 } else {
280 let entries = fs::read_dir(&path).map_err(|err| {
281 format!("parse_sources: fs::read_dir({path:?}) failed: {err}")
282 })?;
283
284 for entry in entries {
285 let path = entry.unwrap().path();
286 if path.is_dir()
287 || (path.is_file() && path.extension().unwrap_or_default() == "aidl")
288 {
289 sources.push(path);
290 }
291 }
292 };
293
294 seen.insert(path);
295 }
296 }
297
298 Ok(document_list)
299 }
300
301 pub fn generate(mut self) -> Result<(), Box<dyn Error>> {
302 let documents = self.parse_sources()?;
303
304 for document in &documents {
308 generator::Generator::pre_register_enums(&document.1);
309 }
310
311 let mut package_list = Vec::new();
313 for document in &documents {
314 println!("Generating: {}", document.0);
315 let gen = generator::Generator::new(self.enabled_async, self.is_crate);
316 let package = gen.document(&document.1)?;
317 package_list.push((package.0, package.1, document.0.clone()));
318 }
319
320 let content = self.generate_all(package_list)?;
321
322 fs::write(self.dest_dir.join(&self.output), content)?;
323
324 Ok(())
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
333
334 #[test]
335 fn test_relative_mod() {
336 let target = Namespace::new("android.os.IServiceCallback", Namespace::AIDL);
337 let curr = Namespace::new("android.os.IServiceManager", Namespace::AIDL);
338
339 assert_eq!(curr.relative_mod(&target), "super::IServiceCallback");
340
341 let target = Namespace::new("android.aidl.test.IServiceCallback", Namespace::AIDL);
342 let curr = Namespace::new("android.os.IServiceManager", Namespace::AIDL);
343
344 assert_eq!(
345 curr.relative_mod(&target),
346 "super::super::aidl::test::IServiceCallback"
347 );
348 }
349}