use std::{
collections::HashMap,
fmt::Debug,
hash::Hash,
io::Read,
path::{Path, PathBuf},
};
use crate::ast;
use crate::diagnostic::Diagnostic;
use crate::rules;
use crate::validation;
pub struct Parser<ID>
where
ID: Eq + Hash + Clone + Debug,
{
lalrpop_results: HashMap<ID, ParseFileResult<ID>>,
}
#[derive(Debug, Clone)]
pub struct ParseFileResult<ID>
where
ID: Eq + Hash + Clone + Debug,
{
pub id: ID,
pub ast: Option<ast::Aidl>,
pub diagnostics: Vec<Diagnostic>,
}
impl<ID> Parser<ID>
where
ID: Eq + Hash + Clone + Debug,
{
pub fn new() -> Self {
Parser {
lalrpop_results: HashMap::new(),
}
}
pub fn add_content(&mut self, id: ID, content: &str) {
let lookup = line_col::LineColLookup::new(content);
let mut diagnostics = Vec::new();
let rule_result =
rules::aidl::OptAidlParser::new().parse(&lookup, &mut diagnostics, content);
let lalrpop_result = match rule_result {
Ok(file) => ParseFileResult {
id: id.clone(),
ast: file,
diagnostics,
},
Err(e) => {
if let Some(diagnostic) = Diagnostic::from_parse_error(&lookup, e) {
diagnostics.push(diagnostic)
}
ParseFileResult {
id: id.clone(),
ast: None,
diagnostics,
}
}
};
self.lalrpop_results.insert(id, lalrpop_result);
}
pub fn remove_content(&mut self, id: ID) {
self.lalrpop_results.remove(&id);
}
pub fn validate(&self) -> HashMap<ID, ParseFileResult<ID>> {
let keys = self.collect_item_keys();
validation::validate(keys, self.lalrpop_results.clone())
}
fn collect_item_keys(&self) -> HashMap<ast::ItemKey, ast::ResolvedItemKind> {
self.lalrpop_results
.values()
.flat_map(|fr| &fr.ast)
.map(|f| (f.get_key(), f.item.get_kind()))
.collect()
}
}
impl Parser<PathBuf> {
pub fn add_file<P: AsRef<Path>>(&mut self, path: P) -> std::io::Result<()> {
let mut file = std::fs::File::open(path.as_ref())?;
let mut buffer = String::new();
file.read_to_string(&mut buffer)?;
self.add_content(PathBuf::from(path.as_ref()), &buffer);
Ok(())
}
}
impl<ID> Default for Parser<ID>
where
ID: Eq + Hash + Clone + Debug,
{
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod test {
use super::*;
use anyhow::Result;
#[test]
fn test_validate() -> Result<()> {
let interface_aidl = r#"
package com.bwa.aidl_test;
import com.bwa.aidl_test.MyEnum;
import com.bwa.aidl_test.MyEnum;
import com.bwa.aidl_test.MyEnum;
import com.bwa.aidl_test.MyParcelable;
import com.bwa.aidl_test.MyUnexisting;
interface MyInterface {
const int MY_CONST = 12;
/**
* Be polite and say hello
*/
//String hello(MyEnum e, MyParcelable);
String servus(MyEnum e, MyWrong);
String bonjour(MyEnum e, MyUnexisting);
}
"#;
let enum_aidl = r#"
package com.bwa.aidl_test;
enum MyEnum {
VALUE1 = 1,
VALUE2 = 2,
}
"#;
let parcelable_aidl = r#"
package com.bwa.aidl_test;
parcelable MyParcelable {
String name;
byte[] data;
}
"#;
let mut parser = Parser::new();
parser.add_content(0, interface_aidl);
parser.add_content(1, parcelable_aidl);
parser.add_content(2, enum_aidl);
let res = parser.validate();
assert_eq!(res.len(), 3);
println!("...\nDiagnostics 1:\n{:#?}", res[&0].diagnostics);
println!("...\nDiagnostics 2:\n{:#?}", res[&1].diagnostics);
println!("...\nDiagnostics 3:\n{:#?}", res[&2].diagnostics);
Ok(())
}
}