use std::path::Path;
#[derive(Clone, Copy)]
pub(crate) struct SourceCompilerAuthority {
trusted_host_dispatch: bool,
}
impl SourceCompilerAuthority {
pub(crate) fn for_source(path: &Path) -> Self {
Self {
trusted_host_dispatch: trusted_host_dispatch_for_source(path),
}
}
pub(crate) fn typechecker(self) -> harn_parser::TypeChecker {
harn_parser::TypeChecker::new().with_privileged_wire_builtins(self.trusted_host_dispatch)
}
pub(crate) fn module_provenance(self) -> harn_vm::module_artifact::ModuleProvenance {
if self.trusted_host_dispatch {
harn_vm::module_artifact::ModuleProvenance::TrustedHostDispatch
} else {
harn_vm::module_artifact::ModuleProvenance::User
}
}
pub(crate) fn compiler_with_imported_enums(
self,
candidates: impl IntoIterator<Item = String>,
) -> harn_vm::Compiler {
let compiler = if self.trusted_host_dispatch {
harn_vm::Compiler::new_trusted_host_dispatch()
} else {
harn_vm::Compiler::new()
};
compiler.with_imported_enum_candidates(candidates)
}
pub(crate) fn compiler_with_imported_symbols(
self,
enum_candidates: impl IntoIterator<Item = String>,
callable_names: impl IntoIterator<Item = String>,
) -> harn_vm::Compiler {
self.compiler_with_imported_enums(enum_candidates)
.with_imported_source_callable_names(callable_names)
}
pub(crate) fn compile_module_with_imported_symbols(
self,
source_path: &Path,
source: &str,
context: &harn_vm::module_artifact::ModuleCompilationContext,
) -> Result<harn_vm::module_artifact::ModuleArtifact, harn_vm::VmError> {
if self.trusted_host_dispatch {
harn_vm::module_artifact::compile_trusted_host_dispatch_module_artifact_from_source_with_context(
source_path,
source,
context,
)
} else {
harn_vm::module_artifact::compile_module_artifact_from_source_with_context(
source_path,
source,
context,
)
}
}
}
pub(crate) fn ensure_builtin_signatures_installed() {
harn_parser::install_builtin_manifest(harn_vm::stdlib::all_builtin_manifest());
}
pub(crate) fn compiler_for_source(path: &Path, source: &str) -> harn_vm::Compiler {
let imported = imported_symbols_for_source(path, source);
SourceCompilerAuthority::for_source(path).compiler_with_imported_symbols(
imported.enum_candidates().iter().cloned(),
imported.source_callable_names().iter().cloned(),
)
}
pub(crate) fn imported_symbols_for_source(
path: &Path,
source: &str,
) -> harn_vm::module_artifact::ModuleCompilationContext {
let graph = harn_modules::build_with_source(path, source);
harn_vm::module_artifact::ModuleCompilationContext::for_source_in_graph(&graph, path, source)
.unwrap_or_default()
}
pub(crate) fn trusted_host_dispatch_for_source(path: &Path) -> bool {
let absolute = path
.canonicalize()
.unwrap_or_else(|_| std::env::current_dir().unwrap_or_default().join(path));
crate::package::load_check_config(Some(&absolute)).trusted_host_dispatch
}
pub(crate) fn enable_trusted_host_dispatch_for_source(
vm: &mut harn_vm::Vm,
path: &Path,
) -> Result<(), harn_vm::VmError> {
if !trusted_host_dispatch_for_source(path) {
return Ok(());
}
vm.enable_trusted_host_dispatch()?;
Ok(())
}
pub(crate) fn compiler_with_imported_enum_candidates(
candidates: impl IntoIterator<Item = String>,
) -> harn_vm::Compiler {
harn_vm::Compiler::new().with_imported_enum_candidates(candidates)
}
#[cfg(test)]
mod tests {
use super::*;
fn authority_fixture(manifest: Option<&str>) -> (tempfile::TempDir, std::path::PathBuf) {
let project = tempfile::tempdir().expect("temp project");
std::fs::create_dir(project.path().join(".git")).expect("project boundary");
if let Some(manifest) = manifest {
std::fs::write(project.path().join("harn.toml"), manifest).expect("manifest fixture");
}
let source = project.path().join("main.harn");
std::fs::write(&source, "pipeline main(harness: Harness) {}\n").expect("source fixture");
(project, source)
}
#[test]
fn manifest_authority_boundary_allows_only_an_explicit_valid_declaration() {
let cases = [
(
"allowed",
Some("[check]\ntrusted_host_dispatch = true\n"),
true,
),
(
"denied",
Some("[check]\ntrusted_host_dispatch = false\n"),
false,
),
("missing", None, false),
(
"malformed",
Some("[check\ntrusted_host_dispatch = true\n"),
false,
),
];
for (case, manifest, expected) in cases {
let (_project, source) = authority_fixture(manifest);
assert_eq!(
trusted_host_dispatch_for_source(&source),
expected,
"{case} manifest authority decision"
);
}
}
}