Skip to main content

sim_lib_standard_core/
claims.rs

1//! Standard-distribution claim predicates and profile/badge claim publishing.
2
3use sim_kernel::{
4    Claim, ClaimKind, ClaimPattern, Cx, LibId, Ref, Result, Symbol, card::card_requires_predicate,
5};
6
7use crate::{FidelityBadge, LanguageProfile};
8
9/// Claim predicate relating a profile to its reader symbol.
10pub fn standard_reader_predicate() -> Symbol {
11    standard_symbol("reader")
12}
13
14/// Claim predicate relating a profile to its lowering symbol.
15pub fn standard_lowering_predicate() -> Symbol {
16    standard_symbol("lowering")
17}
18
19/// Claim predicate relating a profile to its eval-policy symbol.
20pub fn standard_eval_policy_predicate() -> Symbol {
21    standard_symbol("eval-policy")
22}
23
24/// Claim predicate relating a profile to its numeric-tower symbol.
25pub fn standard_numeric_predicate() -> Symbol {
26    standard_symbol("numeric")
27}
28
29/// Claim predicate relating a profile to a capability it requires.
30pub fn standard_capability_predicate() -> Symbol {
31    standard_symbol("capability")
32}
33
34/// Claim predicate relating a profile to a form it does not support.
35pub fn standard_unsupported_predicate() -> Symbol {
36    standard_symbol("unsupported")
37}
38
39/// Publish the full claim set for `profile`: reader, lowering, eval-policy,
40/// numeric tower, capabilities, unsupported forms, and fidelity badges.
41///
42/// Each fact is inserted at most once, so republishing an unchanged profile is a
43/// no-op. The claim store and predicate contracts are defined by the kernel; see
44/// the crate README for the contract sections.
45pub fn publish_profile_claims(cx: &mut Cx, profile: &LanguageProfile) -> Result<()> {
46    publish_profile_claims_with_owner(cx, None, profile)
47}
48
49/// Publish profile claims as part of a loaded lib receipt.
50pub fn publish_profile_claims_for_lib(
51    cx: &mut Cx,
52    lib_id: LibId,
53    profile: &LanguageProfile,
54) -> Result<()> {
55    publish_profile_claims_with_owner(cx, Some(lib_id), profile)
56}
57
58fn publish_profile_claims_with_owner(
59    cx: &mut Cx,
60    owner: Option<LibId>,
61    profile: &LanguageProfile,
62) -> Result<()> {
63    match owner {
64        Some(lib_id) => sim_kernel::standard::publish_profile_claims_for_lib(
65            cx,
66            lib_id,
67            profile.symbol.clone(),
68            profile.organs.iter().map(|organ| organ.organ.clone()),
69            profile.conformance_tests.iter().cloned(),
70        )?,
71        None => sim_kernel::standard::publish_profile_claims(
72            cx,
73            profile.symbol.clone(),
74            profile.organs.iter().map(|organ| organ.organ.clone()),
75            profile.conformance_tests.iter().cloned(),
76        )?,
77    }
78
79    let subject = Ref::Symbol(profile.symbol.clone());
80    insert_once(
81        cx,
82        owner,
83        subject.clone(),
84        standard_reader_predicate(),
85        Ref::Symbol(profile.reader.clone()),
86    )?;
87    insert_once(
88        cx,
89        owner,
90        subject.clone(),
91        standard_lowering_predicate(),
92        Ref::Symbol(profile.lowering.clone()),
93    )?;
94    insert_once(
95        cx,
96        owner,
97        subject.clone(),
98        standard_eval_policy_predicate(),
99        Ref::Symbol(profile.eval_policy.clone()),
100    )?;
101    if let Some(numeric_tower) = &profile.numeric_tower {
102        insert_once(
103            cx,
104            owner,
105            subject.clone(),
106            standard_numeric_predicate(),
107            Ref::Symbol(numeric_tower.clone()),
108        )?;
109    }
110    for capability in &profile.capabilities {
111        insert_once(
112            cx,
113            owner,
114            subject.clone(),
115            standard_capability_predicate(),
116            Ref::Symbol(capability.as_symbol()),
117        )?;
118        insert_once(
119            cx,
120            owner,
121            subject.clone(),
122            card_requires_predicate(),
123            Ref::Symbol(capability.as_symbol()),
124        )?;
125    }
126    for form in &profile.unsupported_forms {
127        insert_once(
128            cx,
129            owner,
130            subject.clone(),
131            standard_unsupported_predicate(),
132            Ref::Symbol(form.clone()),
133        )?;
134    }
135    for badge in &profile.fidelity_badges {
136        publish_badge_claims_with_owner(cx, owner, badge)?;
137    }
138    Ok(())
139}
140
141/// Publish the claim set for a single [`FidelityBadge`]: its badge fact,
142/// fidelity level, and the observed badge claim, each inserted at most once.
143pub fn publish_badge_claims(cx: &mut Cx, badge: &FidelityBadge) -> Result<()> {
144    publish_badge_claims_with_owner(cx, None, badge)
145}
146
147/// Publish badge claims as part of a loaded lib receipt.
148pub fn publish_badge_claims_for_lib(
149    cx: &mut Cx,
150    lib_id: LibId,
151    badge: &FidelityBadge,
152) -> Result<()> {
153    publish_badge_claims_with_owner(cx, Some(lib_id), badge)
154}
155
156fn publish_badge_claims_with_owner(
157    cx: &mut Cx,
158    owner: Option<LibId>,
159    badge: &FidelityBadge,
160) -> Result<()> {
161    match owner {
162        Some(lib_id) => sim_kernel::standard::publish_fidelity_badge_for_lib(
163            cx,
164            lib_id,
165            badge.subject.clone(),
166            badge.badge.clone(),
167            badge.evidence.clone(),
168        )?,
169        None => sim_kernel::standard::publish_fidelity_badge(
170            cx,
171            badge.subject.clone(),
172            badge.badge.clone(),
173            badge.evidence.clone(),
174        )?,
175    }
176    insert_once(
177        cx,
178        owner,
179        badge.subject.clone(),
180        standard_symbol("fidelity-level"),
181        Ref::Symbol(Symbol::qualified(
182            "standard/fidelity-level",
183            badge.level.to_string(),
184        )),
185    )?;
186    insert_observed_once(
187        cx,
188        owner,
189        badge.subject.clone(),
190        standard_symbol("fidelity-badge"),
191        Ref::Symbol(badge.badge.clone()),
192    )
193}
194
195fn insert_once(
196    cx: &mut Cx,
197    owner: Option<LibId>,
198    subject: Ref,
199    predicate: Symbol,
200    object: Ref,
201) -> Result<()> {
202    let exists = !cx
203        .query_facts(ClaimPattern::exact(
204            subject.clone(),
205            predicate.clone(),
206            object.clone(),
207        ))?
208        .is_empty();
209    if !exists {
210        insert_claim(cx, owner, Claim::public(subject, predicate, object))?;
211    }
212    Ok(())
213}
214
215fn insert_observed_once(
216    cx: &mut Cx,
217    owner: Option<LibId>,
218    subject: Ref,
219    predicate: Symbol,
220    object: Ref,
221) -> Result<()> {
222    let exists = !cx
223        .query_facts(ClaimPattern::exact(
224            subject.clone(),
225            predicate.clone(),
226            object.clone(),
227        ))?
228        .is_empty();
229    if !exists {
230        insert_claim(
231            cx,
232            owner,
233            Claim::public(subject, predicate, object).with_kind(ClaimKind::Observed),
234        )?;
235    }
236    Ok(())
237}
238
239fn insert_claim(cx: &mut Cx, owner: Option<LibId>, claim: Claim) -> Result<()> {
240    match owner {
241        Some(lib_id) => {
242            cx.insert_fact_for_lib(lib_id, claim)?;
243        }
244        None => {
245            cx.insert_fact(claim)?;
246        }
247    }
248    Ok(())
249}
250
251fn standard_symbol(name: &str) -> Symbol {
252    Symbol::qualified("standard", name.to_owned())
253}