use std::fmt;
use crate::registry_core::identity::{NodeId, StableFaceId};
#[path = "source_location.rs"]
mod source_location;
pub use source_location::*;
#[path = "contract.rs"]
mod contract;
pub use contract::*;
#[path = "registration.rs"]
mod registration;
pub use registration::*;
#[path = "owned.rs"]
mod owned;
pub use owned::*;
#[path = "runtime_checks.rs"]
mod runtime_checks;
pub use runtime_checks::*;
#[path = "call_evidence.rs"]
mod call_evidence;
pub use call_evidence::*;
pub use crate::registry_core::plugin::contracts::*;
fn provided_contains<S: AsRef<str>>(provided: &[S], required: &S) -> bool {
provided
.iter()
.any(|candidate| candidate.as_ref() == required.as_ref())
}
pub(crate) struct RegistrationRequirementCheck<'a, S> {
required_preset: Option<&'a str>,
required_parts: &'a [S],
required_exports: &'a [S],
required_handle_traits: &'a [S],
required_part_traits: &'a [S],
preset: &'a str,
provided_parts: &'a [S],
exports: &'a [S],
handle: &'a str,
handle_traits: &'a [S],
parts: &'a str,
part_traits: &'a [S],
}
pub(crate) fn validate_registration_requirements<S: AsRef<str>>(
check: RegistrationRequirementCheck<'_, S>,
) -> Vec<String> {
let mut failures = Vec::new();
if let Some(expected) = check.required_preset
&& check.preset != expected
{
failures.push(format!(
"preset `{expected}` is required, received `{}`",
check.preset
));
}
for required in check.required_parts {
if !provided_contains(check.provided_parts, required) {
failures.push(format!(
"required structural part `{}` is missing",
required.as_ref()
));
}
}
for required in check.required_exports {
if !provided_contains(check.exports, required) {
failures.push(format!(
"required export `{}` is missing",
required.as_ref()
));
}
}
for required in check.required_handle_traits {
if !provided_contains(check.handle_traits, required) {
failures.push(format!(
"handle `{}` must implement interface `{}`",
check.handle,
required.as_ref()
));
}
}
for required in check.required_part_traits {
if !provided_contains(check.part_traits, required) {
failures.push(format!(
"parts `{}` must implement interface `{}`",
check.parts,
required.as_ref()
));
}
}
failures
}
pub(crate) fn validate_object_contract<S: AsRef<str>>(
required_parts: &[S],
provided_parts: &[S],
object: &str,
) -> Vec<String> {
let mut failures = Vec::new();
for required in required_parts {
if !provided_contains(provided_parts, required) {
failures.push(format!(
"`{object}` is missing construction part `{}`",
required.as_ref()
));
}
}
failures
}
#[cfg(test)]
mod tests {
use super::*;
use crate::registry_core::identity::root_node_id;
fn info(namespace: &'static str, kind: &'static str) -> RegistrationInfo {
RegistrationInfo {
namespace,
id: NodeId::from_namespaced_path(namespace, "src/item.rs", kind),
parent: root_node_id(namespace),
kind,
preset: "NoPreset",
parts: "NoParts",
params: "Params",
handle: "Handle",
stable_name: None,
name: LocalizedText {
zh: "名称",
en: "Name",
},
summary: LocalizedText { zh: "", en: "" },
exports: &[],
needs_registry: false,
registry_name: kind,
getting_from_other_registry: None,
registry_rule_path: "<test>",
registry_rule: RegistrationRule::ANY,
admission: Admission::ANY,
requires: &[],
provides: &[],
contract: ObjectContract {
required_parts: &["paint"],
provided_parts: &[],
},
flow: FlowContract::NONE,
flow_provider: None,
handle_traits: &[],
part_traits: &[],
runtime_checks: &[],
plugin: None,
source: SourceLocation {
file: "src/item.rs",
line: 1,
column: 1,
function: kind,
},
}
}
fn owned_snapshot(namespace: &str, kind: &str) -> RegistrationSnapshot {
RegistrationSnapshot {
namespace: namespace.to_owned(),
id: NodeId::from_namespaced_path(namespace, "src/item.rs", kind),
parent: root_node_id(namespace),
kind: kind.to_owned(),
preset: "NoPreset".to_owned(),
parts: "NoParts".to_owned(),
params: "Params".to_owned(),
handle: "Handle".to_owned(),
stable_name: None,
name: OwnedLocalizedText {
zh: "名称".to_owned(),
en: "Name".to_owned(),
},
summary: OwnedLocalizedText {
zh: String::new(),
en: String::new(),
},
exports: Vec::new(),
needs_registry: false,
registry_name: kind.to_owned(),
getting_from_other_registry: None,
registry_rule_path: "<test>".to_owned(),
registry_rule: OwnedRegistrationRule {
required_preset: None,
required_parts: Vec::new(),
required_exports: Vec::new(),
required_handle_traits: Vec::new(),
required_part_traits: Vec::new(),
},
admission: Admission::ANY.into_owned(),
requires: Vec::new(),
provides: Vec::new(),
contract: OwnedObjectContract {
required_parts: vec!["paint".to_owned()],
provided_parts: Vec::new(),
},
flow: OwnedFlowContract::none(),
flow_provider: None,
handle_traits: Vec::new(),
part_traits: Vec::new(),
runtime_checks: Vec::new(),
plugin: None,
source: OwnedSourceLocation {
file: "src/item.rs".to_owned(),
line: 1,
column: 1,
function: kind.to_owned(),
},
}
}
#[test]
fn registration_rule_twins_report_identical_failures() {
let namespace = "declaration-twins";
let rule = RegistrationRule::new()
.require_preset("ActionParts")
.require_parts(&["paint"])
.require_exports(&["control.render"])
.require_handle_traits(&["ControlHandle"])
.require_part_traits(&["ActionParts"]);
let compiled_info = info(namespace, "Button");
let mut reloaded = owned_snapshot(namespace, "Button");
reloaded.registry_rule = rule.into_owned();
let compiled = rule.validate(&compiled_info);
let owned = reloaded.registry_rule.validate(&reloaded);
assert_eq!(compiled, owned, "compiled and reloaded disagree");
for message in [
"preset `ActionParts` is required, received `NoPreset`",
"required structural part `paint` is missing",
"required export `control.render` is missing",
"handle `Handle` must implement interface `ControlHandle`",
"parts `NoParts` must implement interface `ActionParts`",
] {
assert!(
compiled.iter().any(|failure| failure == message),
"missing diagnostic: {message}\n{compiled:?}"
);
}
}
#[test]
fn object_contract_twins_report_identical_failures() {
let compiled_contract = ObjectContract {
required_parts: &["paint", "layout"],
provided_parts: &["layout"],
};
let owned_contract = OwnedObjectContract {
required_parts: vec!["paint".to_owned(), "layout".to_owned()],
provided_parts: vec!["layout".to_owned()],
};
let compiled = compiled_contract.validate("Button");
let owned = owned_contract.validate("Button");
assert_eq!(compiled, owned, "compiled and owned disagree");
assert_eq!(
compiled,
["`Button` is missing construction part `paint`".to_owned()]
);
}
#[test]
fn admission_twins_accept_and_reject_identical_paths() {
let compiled = Admission::new(&["ui", "ui/controls"], &["ui/experimental"]);
let reloaded = compiled.into_owned();
for path in [
"ui",
"ui/controls",
"ui/controls/Button",
"ui/experimental",
"ui/experiment",
"ui2",
"graphics",
"",
] {
assert_eq!(
compiled.accepts(path),
reloaded.accepts(path),
"admission twins disagree on `{path}`"
);
}
}
}