schemaorg_rs/profiles/
engine.rs1use crate::graph::StructuredDataGraph;
4use crate::validation::ValidationDiagnostic;
5use crate::vocabulary;
6
7use super::{Eligibility, Profile, ProfileResult, TypeEligibility};
8
9pub fn evaluate_graph(
15 profile: &dyn Profile,
16 graph: &StructuredDataGraph,
17 vocab_diagnostics: &[ValidationDiagnostic],
18) -> ProfileResult {
19 let mut type_results = Vec::new();
20 let mut all_diagnostics = Vec::new();
21
22 for node in &graph.nodes {
23 let dominated = node.types.iter().any(|t| {
25 profile
26 .supported_types()
27 .iter()
28 .any(|supported| t == supported || vocabulary::is_subtype(t, supported))
29 });
30
31 if !dominated {
32 continue;
33 }
34
35 let result = profile.evaluate_node(node, vocab_diagnostics);
36 all_diagnostics.extend(result.type_eligibility.field_diagnostics.iter().cloned());
37 type_results.push(result.type_eligibility);
38 }
39
40 let eligibility = aggregate_eligibility(&type_results, &all_diagnostics);
41
42 ProfileResult {
43 eligibility,
44 type_results,
45 diagnostics: all_diagnostics,
46 }
47}
48
49pub(crate) fn aggregate_eligibility(
51 type_results: &[TypeEligibility],
52 diagnostics: &[ValidationDiagnostic],
53) -> Eligibility {
54 if type_results.is_empty() {
55 return Eligibility::NotEligible;
56 }
57
58 let has_restricted = diagnostics
60 .iter()
61 .any(|d| d.code == crate::validation::DiagnosticCode::EligibilityRestricted);
62
63 let all_eligible = type_results.iter().all(|r| r.eligible);
64 let has_warnings = !diagnostics.is_empty();
65
66 if has_restricted {
67 Eligibility::Restricted
68 } else if all_eligible && !has_warnings {
69 Eligibility::Eligible
70 } else if all_eligible {
71 Eligibility::WarningsOnly
72 } else {
73 Eligibility::NotEligible
74 }
75}