use std::collections::HashSet;
use semver::Version;
use crate::compilation::internal_builder::{AddFileResponse, InternalCompilationBuilder};
use crate::compilation::CompilationUnit;
use crate::cst::Cursor;
pub trait CompilationBuilderConfig {
type Error;
fn read_file(&mut self, file_id: &str) -> Result<Option<String>, Self::Error>;
fn resolve_import(
&mut self,
source_file_id: &str,
import_path_cursor: &Cursor,
) -> Result<Option<String>, Self::Error>;
}
pub struct CompilationBuilder<E, C: CompilationBuilderConfig<Error = E>> {
pub config: C,
internal: InternalCompilationBuilder,
seen_files: HashSet<String>,
}
#[derive(thiserror::Error, Debug)]
pub enum CompilationInitializationError {
#[error("Unsupported language version '{0}'.")]
UnsupportedLanguageVersion(Version),
}
impl<E, C: CompilationBuilderConfig<Error = E>> CompilationBuilder<E, C> {
pub fn create(
version: Version,
config: C,
) -> Result<CompilationBuilder<E, C>, CompilationInitializationError> {
let internal = InternalCompilationBuilder::create(version.clone())
.map_err(|_| CompilationInitializationError::UnsupportedLanguageVersion(version))?;
Ok(CompilationBuilder {
config,
internal,
seen_files: HashSet::new(),
})
}
pub fn add_file(&mut self, file_id: &str) -> Result<(), E> {
if !self.seen_files.insert(file_id.into()) {
return Ok(());
}
let source = self.config.read_file(file_id)?;
if let Some(source) = source {
let AddFileResponse { import_paths } = self.internal.add_file(file_id.into(), &source);
for import_path_cursor in import_paths {
let import_id = self.config.resolve_import(file_id, &import_path_cursor)?;
if let Some(import_id) = &import_id {
self.internal
.resolve_import(file_id, &import_path_cursor, import_id.clone())
.unwrap_or_else(|_| panic!("{file_id} should have been added"));
self.add_file(import_id)?;
}
}
}
Ok(())
}
pub fn build(&self) -> CompilationUnit {
self.internal.build()
}
}