use std::collections::HashMap;
use std::path::PathBuf;
use crate::error::Error;
use crate::parser::parse_file_with_deps;
use crate::types::{ValidatedDef, ValidatedSubDecoder};
use crate::validate::validate;
use super::types::BindingsFile;
pub struct ResolvedBindings {
pub file: BindingsFile,
pub all_files: Vec<PathBuf>,
pub specs: Vec<ResolvedSpec>,
}
pub struct ResolvedSpec {
pub path: PathBuf,
pub def: ValidatedDef,
}
impl ResolvedBindings {
pub fn find_decoder(&self, name: &str) -> Option<&ResolvedSpec> {
self.specs.iter().find(|s| s.def.config.name == name)
}
pub fn find_subdecoder(&self, name: &str) -> Option<(&ResolvedSpec, &ValidatedSubDecoder)> {
for spec in &self.specs {
for sd in &spec.def.sub_decoders {
if sd.name == name {
return Some((spec, sd));
}
}
}
None
}
pub fn find_decoder_or_sub(
&self,
name: &str,
) -> Option<(&ResolvedSpec, Option<&ValidatedSubDecoder>)> {
if let Some(s) = self.find_decoder(name) {
return Some((s, None));
}
if let Some((s, sd)) = self.find_subdecoder(name) {
return Some((s, Some(sd)));
}
None
}
pub fn all_decoder_names(&self) -> Vec<String> {
let mut names = Vec::new();
for spec in &self.specs {
names.push(spec.def.config.name.clone());
for sd in &spec.def.sub_decoders {
names.push(sd.name.clone());
}
}
names
}
}
pub fn resolve(file: BindingsFile) -> Result<ResolvedBindings, Vec<Error>> {
let mut all_files: Vec<PathBuf> = Vec::new();
all_files.push(file.path.clone());
for (p, _) in &file.bindings_includes {
all_files.push(p.clone());
}
let mut seen: HashMap<PathBuf, ()> = HashMap::new();
let mut specs: Vec<ResolvedSpec> = Vec::new();
let mut errors: Vec<Error> = Vec::new();
for (path, _span) in &file.spec_includes {
if seen.contains_key(path) {
continue;
}
seen.insert(path.clone(), ());
let (def, deps) = match parse_file_with_deps(path) {
Ok(v) => v,
Err(errs) => {
errors.extend(errs);
continue;
}
};
for dep in deps {
if !all_files.contains(&dep) {
all_files.push(dep);
}
}
if !all_files.contains(path) {
all_files.push(path.clone());
}
match validate(&def) {
Ok(v) => specs.push(ResolvedSpec {
path: path.clone(),
def: v,
}),
Err(errs) => errors.extend(errs),
}
}
if !errors.is_empty() {
return Err(errors);
}
Ok(ResolvedBindings {
file,
all_files,
specs,
})
}