evidence.mojo (2627B)
1 from std.collections import Optional 2 3 4 @fieldwise_init 5 struct EvidenceRef(Copyable, Movable): 6 var source_id: String 7 var revision: String 8 var kind: String 9 var start: Int 10 var end: Int 11 var record_id: Optional[String] 12 var field: Optional[String] 13 var method: String 14 15 16 def span_evidence( 17 source_id: String, revision: String, start: Int, end: Int, method: String 18 ) raises -> EvidenceRef: 19 if source_id.strip() == "": 20 raise Error("evidence source_id must not be empty") 21 if revision.strip() == "": 22 raise Error("evidence revision must not be empty") 23 if method.strip() == "": 24 raise Error("evidence method must not be empty") 25 if start < 0: 26 raise Error("evidence span start must be non-negative") 27 if end < start: 28 raise Error("evidence span end must not precede start") 29 return EvidenceRef( 30 source_id=String(source_id), 31 revision=String(revision), 32 kind="span", 33 start=start, 34 end=end, 35 record_id=None, 36 field=None, 37 method=String(method), 38 ) 39 40 41 def record_field_evidence( 42 source_id: String, revision: String, record_id: String, field: String 43 ) raises -> EvidenceRef: 44 if source_id.strip() == "" or revision.strip() == "": 45 raise Error("evidence source_id and revision must not be empty") 46 if record_id.strip() == "" or field.strip() == "": 47 raise Error("record_field evidence requires record_id and field") 48 return EvidenceRef( 49 source_id=String(source_id), 50 revision=String(revision), 51 kind="record_field", 52 start=0, 53 end=0, 54 record_id=Optional[String](String(record_id)), 55 field=Optional[String](String(field)), 56 method="trusted_record", 57 ) 58 59 60 def span_is_valid(text: String, evidence: EvidenceRef) raises -> Bool: 61 if evidence.kind != "span": 62 raise Error("span_is_valid requires span evidence") 63 return evidence.end <= text.byte_length() 64 65 66 def validate_span_against_revision( 67 text: String, source_revision: String, evidence: EvidenceRef 68 ) raises: 69 if evidence.kind != "span": 70 raise Error("span evidence required") 71 if evidence.revision != source_revision: 72 raise Error("evidence revision does not match source revision") 73 if not span_is_valid(text, evidence): 74 raise Error("evidence span exceeds source byte length") 75 76 77 def selected_text(text: String, evidence: EvidenceRef) raises -> String: 78 if evidence.kind != "span": 79 raise Error("selected_text requires span evidence") 80 return String(text[byte = evidence.start : evidence.end])