use std::io;
use std::path::Path;
use std::sync::Arc;
use brink_driver::{AnalysisOptions, Driver, RealFs};
use brink_ir::Diagnostic;
use tracing::{info, warn};
use crate::{CompileError, CompileOutput, ResolvedDiagnostic};
fn resolve_diagnostics(driver: &Driver, diags: Vec<Diagnostic>) -> Vec<ResolvedDiagnostic> {
let db = driver.db();
let opts = db.analysis_options();
let types = opts.type_policy();
diags
.into_iter()
.filter_map(|d| {
let severity = brink_driver::effective_severity(d.code, types, &opts.lints)?;
Some((d, severity))
})
.map(|(d, severity)| ResolvedDiagnostic {
path: db.file_path(d.file).unwrap_or_default().to_string(),
file: d.file,
range: d.range,
message: d.message,
severity,
code: d.code,
})
.collect()
}
fn prepare_driver<F>(
entry: &str,
read_file: F,
options: AnalysisOptions,
) -> Result<(Driver, brink_ir::FileId), CompileError>
where
F: FnMut(&str) -> Result<String, io::Error>,
{
info!(entry, "starting compilation");
let mut driver = Driver::new();
driver.set_analysis_options(options);
let entry_key = if brink_driver::is_native(Path::new(entry)) {
let (root, warnings) = brink_driver::native_source_root_with_warnings(Path::new(entry));
for warning in &warnings {
warn!("{warning}");
}
let tree = RealFs::new(&root);
driver.discover_native(&tree)?;
brink_driver::relative_key(&root, Path::new(entry))
} else {
driver.discover(entry, read_file)?;
let (root, warnings) = brink_driver::native_source_root_with_warnings(Path::new(entry));
for warning in &warnings {
warn!("{warning}");
}
driver
.db_mut()
.set_ink_root(Some(root.to_string_lossy().into_owned()));
entry.to_string()
};
let file_count = driver.db().file_ids().count();
info!(file_count, "all files discovered");
let entry_id = driver.db_mut().set_entry(&entry_key).ok_or_else(|| {
CompileError::Io(io::Error::new(
io::ErrorKind::NotFound,
format!("entry file not found after discovery: {entry_key}"),
))
})?;
Ok((driver, entry_id))
}
pub fn compile_with_options<F>(
entry: &str,
read_file: F,
options: AnalysisOptions,
) -> Result<CompileOutput, CompileError>
where
F: FnMut(&str) -> Result<String, io::Error>,
{
let (driver, _entry_id) = prepare_driver(entry, read_file, options)?;
let product = driver.db().story_data().cloned().unwrap_or_default();
let Some(story) = product.story else {
let mut all = product.errors;
all.extend(product.warnings);
return Err(CompileError::Diagnostics(resolve_diagnostics(&driver, all)));
};
Ok(CompileOutput {
data: Arc::unwrap_or_clone(story),
warnings: resolve_diagnostics(&driver, product.warnings),
})
}