made_core/value_objects/
evidence_grounding_rule.rs1use std::collections::BTreeSet;
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::DomainError;
6use crate::value_objects::SemanticSupportRule;
7
8use super::output_contract_validation::{
9 validate_text, MAX_ALLOWED_EVIDENCE_REFS, MAX_ALLOWED_VALUE_LEN, MAX_FIELD_NAME_LEN,
10};
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct EvidenceGroundingRule {
15 claims_field: String,
16 refs_field: String,
17 allowed_refs: BTreeSet<String>,
18 #[serde(default, skip_serializing_if = "Option::is_none")]
19 semantic_support: Option<SemanticSupportRule>,
20}
21
22impl EvidenceGroundingRule {
23 pub fn new(
24 claims_field: impl Into<String>,
25 refs_field: impl Into<String>,
26 allowed_refs: impl IntoIterator<Item = impl Into<String>>,
27 ) -> Result<Self, DomainError> {
28 let claims_field = validate_text(
29 &claims_field.into(),
30 "output_contract.evidence.claims_field",
31 MAX_FIELD_NAME_LEN,
32 )?;
33 let refs_field = validate_text(
34 &refs_field.into(),
35 "output_contract.evidence.refs_field",
36 MAX_FIELD_NAME_LEN,
37 )?;
38 let allowed_refs = allowed_refs
39 .into_iter()
40 .map(|reference| {
41 validate_text(
42 &reference.into(),
43 "output_contract.evidence.allowed_ref",
44 MAX_ALLOWED_VALUE_LEN,
45 )
46 })
47 .collect::<Result<BTreeSet<_>, _>>()?;
48 if allowed_refs.is_empty() {
49 return Err(DomainError::EmptyField {
50 field: "output_contract.evidence.allowed_refs",
51 });
52 }
53 if allowed_refs.len() > MAX_ALLOWED_EVIDENCE_REFS {
54 return Err(DomainError::OutOfRange {
55 field: "output_contract.evidence.allowed_refs",
56 value: allowed_refs.len() as f64,
57 min: 1.0,
58 max: MAX_ALLOWED_EVIDENCE_REFS as f64,
59 });
60 }
61 Ok(Self {
62 claims_field,
63 refs_field,
64 allowed_refs,
65 semantic_support: None,
66 })
67 }
68
69 pub fn with_semantic_support(mut self, rule: SemanticSupportRule) -> Result<Self, DomainError> {
70 if self
71 .allowed_refs
72 .iter()
73 .any(|reference| !rule.bodies.contains_key(reference))
74 {
75 return Err(DomainError::EmptyField {
76 field: "output_contract.evidence.semantic_support.bodies",
77 });
78 }
79 self.semantic_support = Some(rule);
80 Ok(self)
81 }
82
83 #[must_use]
84 pub fn claims_field(&self) -> &str {
85 &self.claims_field
86 }
87
88 #[must_use]
89 pub fn refs_field(&self) -> &str {
90 &self.refs_field
91 }
92
93 #[must_use]
94 pub fn allowed_refs(&self) -> &BTreeSet<String> {
95 &self.allowed_refs
96 }
97
98 #[must_use]
99 pub fn semantic_support(&self) -> Option<&SemanticSupportRule> {
100 self.semantic_support.as_ref()
101 }
102}