use guibiao::constitution_json;
use hunyi::{
ASYNC_EXPOSURE_RULE, AsyncExposureBoundary, DYN_TRAIT_RULE, DynTraitBoundary,
FORBIDDEN_MARKER_RULE, ForbiddenMarkerBoundary, IMPL_TRAIT_RULE, ImplTraitBoundary,
SIGNATURE_RULE, SemanticBoundary, TRAIT_IMPL_RULE, TraitImplBoundary, UNSAFE_CONFINEMENT_RULE,
UnsafeBoundary, VisibilityBoundary,
};
use louke::{RUNTIME_SEAM_RULE, RuntimeBoundary};
use serde_json::Value;
use crate::Constitution;
fn anchored(mut object: Value, anchor: Option<&str>) -> Value {
if let Some(anchor) = anchor {
object["anchor"] = serde_json::json!(anchor);
}
object
}
fn boundary_json_base(
kind: &str,
target: &str,
krate: Option<&str>,
rule: &str,
severity: &str,
reason: &str,
anchor: Option<&str>,
) -> Value {
let mut object = serde_json::json!({
"kind": kind,
"target": target,
"rule": rule,
"severity": severity,
"reason": reason,
});
if let Some(krate) = krate {
object["crate"] = serde_json::json!(krate);
}
anchored(object, anchor)
}
fn semantic_module_json(
module: &str,
krate: &str,
rule: &str,
severity: &str,
reason: &str,
anchor: Option<&str>,
) -> Value {
boundary_json_base(
"semantic",
module,
Some(krate),
rule,
severity,
reason,
anchor,
)
}
pub(in crate::runner) fn semantic_boundary_json(boundary: &SemanticBoundary) -> Value {
let mut object = semantic_module_json(
boundary.module(),
boundary.crate_package(),
SIGNATURE_RULE,
boundary.severity().as_str(),
boundary.reason(),
boundary.anchor(),
);
object["forbidden"] = serde_json::json!(boundary.forbidden());
if boundary.including_trait_impls() {
object["including_trait_impls"] = serde_json::json!(true);
}
object
}
pub(in crate::runner) fn trait_impl_boundary_json(boundary: &TraitImplBoundary) -> Value {
let mut object = boundary_json_base(
"semantic",
boundary.trait_(),
Some(boundary.crate_package()),
TRAIT_IMPL_RULE,
boundary.severity().as_str(),
boundary.reason(),
boundary.anchor(),
);
object["allowed_locations"] = serde_json::json!(boundary.allowed_locations());
object
}
pub(in crate::runner) fn visibility_boundary_json(boundary: &VisibilityBoundary) -> Value {
semantic_module_json(
boundary.module(),
boundary.crate_package(),
boundary.ceiling().rule(),
boundary.severity().as_str(),
boundary.reason(),
boundary.anchor(),
)
}
pub(in crate::runner) fn forbidden_marker_boundary_json(
boundary: &ForbiddenMarkerBoundary,
) -> Value {
let mut object = semantic_module_json(
boundary.module(),
boundary.crate_package(),
FORBIDDEN_MARKER_RULE,
boundary.severity().as_str(),
boundary.reason(),
boundary.anchor(),
);
object["forbidden"] = serde_json::json!(boundary.forbidden());
object
}
pub(in crate::runner) fn dyn_trait_boundary_json(boundary: &DynTraitBoundary) -> Value {
let mut object = semantic_module_json(
boundary.module(),
boundary.crate_package(),
DYN_TRAIT_RULE,
boundary.severity().as_str(),
boundary.reason(),
boundary.anchor(),
);
let operands = boundary.forbidden_operands();
if !operands.is_empty() {
object["forbidden"] = serde_json::json!(operands);
}
object
}
pub(in crate::runner) fn impl_trait_boundary_json(boundary: &ImplTraitBoundary) -> Value {
let mut object = semantic_module_json(
boundary.module(),
boundary.crate_package(),
IMPL_TRAIT_RULE,
boundary.severity().as_str(),
boundary.reason(),
boundary.anchor(),
);
let operands = boundary.forbidden_operands();
if !operands.is_empty() {
object["forbidden"] = serde_json::json!(operands);
}
object
}
pub(in crate::runner) fn async_exposure_boundary_json(boundary: &AsyncExposureBoundary) -> Value {
let mut object = semantic_module_json(
boundary.module(),
boundary.crate_package(),
ASYNC_EXPOSURE_RULE,
boundary.severity().as_str(),
boundary.reason(),
boundary.anchor(),
);
if boundary.including_submodules() {
object["including_submodules"] = serde_json::json!(true);
}
object
}
pub(in crate::runner) fn unsafe_boundary_json(boundary: &UnsafeBoundary) -> Value {
let mut object = boundary_json_base(
"semantic",
boundary.crate_package(),
Some(boundary.crate_package()),
UNSAFE_CONFINEMENT_RULE,
boundary.severity().as_str(),
boundary.reason(),
boundary.anchor(),
);
object["allowed_locations"] = serde_json::json!(boundary.allowed_locations());
object
}
fn append_array<T>(document: &mut Value, key: &str, items: &[T], project: impl Fn(&T) -> Value) {
if !items.is_empty() {
document[key] = Value::Array(items.iter().map(project).collect());
}
}
pub(in crate::runner) fn list_document(constitution: &Constitution) -> Value {
let semantic = constitution.semantic_boundaries();
let runtime = constitution.runtime_boundaries();
let mut document: Value =
serde_json::from_str(&constitution_json(constitution.static_boundaries()))
.expect("constitution_json emits a valid document");
append_array(
&mut document,
"semantic_boundaries",
&semantic.signature,
semantic_boundary_json,
);
append_array(
&mut document,
"trait_impl_boundaries",
&semantic.trait_impl,
trait_impl_boundary_json,
);
append_array(
&mut document,
"visibility_boundaries",
&semantic.visibility,
visibility_boundary_json,
);
append_array(
&mut document,
"forbidden_marker_boundaries",
&semantic.forbidden_marker,
forbidden_marker_boundary_json,
);
append_array(
&mut document,
"dyn_trait_boundaries",
&semantic.dyn_trait,
dyn_trait_boundary_json,
);
append_array(
&mut document,
"impl_trait_boundaries",
&semantic.impl_trait,
impl_trait_boundary_json,
);
append_array(
&mut document,
"async_exposure_boundaries",
&semantic.async_exposure,
async_exposure_boundary_json,
);
append_array(
&mut document,
"unsafe_confinement_boundaries",
&semantic.unsafe_confinement,
unsafe_boundary_json,
);
append_array(
&mut document,
"runtime_boundaries",
runtime,
runtime_boundary_json,
);
document
}
pub(in crate::runner) fn runtime_boundary_json(boundary: &RuntimeBoundary) -> Value {
let mut object = boundary_json_base(
"runtime",
boundary.seam(),
None,
RUNTIME_SEAM_RULE,
boundary.severity().as_str(),
boundary.reason(),
boundary.anchor(),
);
object["posture"] = serde_json::json!(boundary.posture().as_str());
object["allowed_origins"] = serde_json::json!(boundary.allowed_origins());
object
}