use std::path::{Path, PathBuf};
use serde_json::Value;
use xuanji::{Outcome, Polarity, Violation};
use crate::containment::matches_allowed;
use crate::driver::run_boundaries;
use crate::dsl::UnsafeBoundary;
use crate::emit::{MultiModuleViolationContext, push_multi_module_violations};
use crate::errors::{unsafe_crate_root_allowed_error, unsafe_empty_allowed_error};
use crate::file_scope::resolve_crate_units;
use crate::finding::{SemanticFact, sort_attributed_facts};
use crate::resolve::{canonical_path_str, validate_path_operands};
use crate::rules::UNSAFE_CONFINEMENT_RULE;
use crate::scan::scan_unsafe_sites;
pub fn check_unsafe_confinement(boundaries: &[UnsafeBoundary], manifest_path: &Path) -> Outcome {
run_boundaries(boundaries, manifest_path, check_unsafe_boundary)
}
pub(crate) fn check_unsafe_boundary(
metadata: &Value,
boundary: &UnsafeBoundary,
violations: &mut Vec<Violation>,
) -> Result<(), String> {
let (_package, units) = resolve_crate_units(metadata, &boundary.crate_package)?;
for (root_file, src_dir, unit) in &units {
let src_dir = src_dir.as_path();
let unit = unit.as_str();
let allowed: Vec<String> = boundary
.allowed_locations
.iter()
.map(|a| canonical_path_str(a))
.collect();
let findings = unsafe_findings(src_dir, root_file, &allowed, &boundary.crate_package)?;
push_multi_module_violations(
violations,
MultiModuleViolationContext {
target: &boundary.crate_package,
rule: UNSAFE_CONFINEMENT_RULE,
rule_key: boundary.rule_key(),
reason: &boundary.reason,
severity: boundary.severity,
anchor: boundary.anchor(),
polarity: Polarity::AllowlistGap,
crate_package: &boundary.crate_package,
unit,
},
findings,
);
}
Ok(())
}
pub(crate) fn unsafe_findings(
src_dir: &Path,
root_file: &Path,
allowed: &[String],
crate_package: &str,
) -> Result<Vec<(SemanticFact, String, PathBuf)>, String> {
if allowed.is_empty() {
return Err(unsafe_empty_allowed_error(crate_package));
}
if allowed.iter().any(|a| a == "crate") {
return Err(unsafe_crate_root_allowed_error(crate_package));
}
validate_path_operands(allowed)?;
let sites = scan_unsafe_sites(src_dir, root_file, crate_package)?;
let mut findings: Vec<(SemanticFact, String, PathBuf)> = sites
.into_iter()
.filter(|site| !matches_allowed(&site.module, allowed))
.map(|site| {
let module = site.module;
(
SemanticFact::UnsafeSite {
module: module.clone(),
site: site.site,
},
module,
site.file,
)
})
.collect();
sort_attributed_facts(&mut findings)?;
Ok(findings)
}