mod diagnostics;
mod discover;
use std::collections::HashMap;
use std::io;
pub use brink_analyzer::{AnalysisOptions, AnalysisResult};
pub use brink_db::ProjectDb;
pub use brink_ir::FileId;
pub use diagnostics::DiagnosticReport;
pub use discover::DiscoverError;
pub struct Driver {
db: ProjectDb,
analysis: Option<AnalysisResult>,
analysis_options: AnalysisOptions,
}
impl Driver {
pub fn new() -> Self {
Self {
db: ProjectDb::new(),
analysis: None,
analysis_options: AnalysisOptions::default(),
}
}
pub fn from_db(db: ProjectDb) -> Self {
Self {
db,
analysis: None,
analysis_options: AnalysisOptions::default(),
}
}
pub fn set_analysis_options(&mut self, options: AnalysisOptions) {
self.analysis_options = options;
self.analysis = None;
}
pub fn db(&self) -> &ProjectDb {
&self.db
}
pub fn db_mut(&mut self) -> &mut ProjectDb {
self.analysis = None;
&mut self.db
}
pub fn into_db(self) -> ProjectDb {
self.db
}
pub fn discover<F>(&mut self, entry: &str, read_file: F) -> Result<(), DiscoverError>
where
F: FnMut(&str) -> Result<String, io::Error>,
{
discover::discover(&mut self.db, entry, &mut { read_file })
}
#[expect(
clippy::expect_used,
reason = "analysis is always Some after the if-block above"
)]
pub fn analyze(&mut self) -> &AnalysisResult {
if self.analysis.is_none() {
let inputs = self.db.analysis_inputs();
let files: Vec<_> = inputs
.iter()
.map(|(id, hir, manifest)| (*id, hir, manifest))
.collect();
tracing::info!(files = files.len(), "running cross-file analysis");
self.analysis = Some(brink_analyzer::analyze_with_options(
&files,
&self.analysis_options,
));
}
self.analysis.as_ref().expect("just set above")
}
pub fn analyze_project(&self, file_ids: &[FileId]) -> AnalysisResult {
let inputs = self.db.analysis_inputs_for(file_ids);
let file_refs: Vec<_> = inputs
.iter()
.map(|(id, hir, manifest)| (*id, hir, manifest))
.collect();
brink_analyzer::analyze(&file_refs)
}
pub fn analysis_inputs_for(
&self,
file_ids: &[FileId],
) -> Vec<(FileId, brink_ir::HirFile, brink_ir::SymbolManifest)> {
self.db.analysis_inputs_for(file_ids)
}
pub fn analysis_inputs(&self) -> Vec<(FileId, brink_ir::HirFile, brink_ir::SymbolManifest)> {
self.db.analysis_inputs()
}
pub fn compute_projects(&self) -> Vec<(FileId, Vec<FileId>)> {
self.db.compute_projects()
}
pub fn file_ids_topo(&self, entry: FileId) -> Vec<FileId> {
self.db.file_ids_topo(entry)
}
pub fn file_metadata(&self) -> Vec<(FileId, String, String)> {
self.db.file_metadata()
}
pub fn collect_diagnostics(
&self,
analysis: &AnalysisResult,
entry: Option<FileId>,
) -> DiagnosticReport {
diagnostics::collect_diagnostics(&self.db, analysis, entry)
}
pub fn lir_inputs(
&self,
entry: FileId,
) -> (Vec<(FileId, &brink_ir::HirFile)>, HashMap<FileId, String>) {
let ids = self.file_ids_topo(entry);
let files: Vec<_> = ids
.into_iter()
.filter_map(|id| self.db.hir(id).map(|hir| (id, hir)))
.collect();
let paths: HashMap<_, _> = files
.iter()
.filter_map(|(id, _)| self.db.file_path(*id).map(|p| (*id, p.to_string())))
.collect();
(files, paths)
}
}
impl Default for Driver {
fn default() -> Self {
Self::new()
}
}