1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// @generated by idiolect-codegen. do not edit.
// source: dev.idiolect.correction
//! A signed record of a post-translation edit. Corrections are the primary signal an observer uses to detect lens quality issues; the reason taxonomy decouples 'lens was wrong' from 'the world is complicated'.
#![allow(
missing_docs,
clippy::doc_markdown,
clippy::struct_excessive_bools,
clippy::derive_partial_eq_without_eq,
clippy::large_enum_variant
)]
use serde::{Deserialize, Serialize};
/// A specific edit to the output of an encounter, attributed by reason.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Correction {
/// Structured grounding for the edit. Useful when `holder` is a third party and the record must state on what basis the correction is being attributed.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub basis: Option<crate::generated::dev::idiolect::defs::Basis>,
/// The value after correction.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub corrected_value: Option<serde_json::Value>,
/// The encounter whose output was edited.
pub encounter: crate::generated::dev::idiolect::defs::EncounterRef,
/// DID of the party the correction is attributed to. Omit for first-party records; set explicitly when a third party is recording someone else's edit (e.g. a reviewer transcribing an off-network correction).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub holder: Option<idiolect_records::Did>,
pub occurred_at: idiolect_records::Datetime,
/// The value prior to correction. May be elided for visibility reasons.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub original_value: Option<serde_json::Value>,
/// JSON Pointer or equivalent path into the produced output identifying the edited location.
pub path: String,
/// Optional human-readable justification for the correction.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rationale: Option<String>,
/// Open-enum reason slug. Resolved against `reasonVocab` when present; aggregators filter or weight by reason.
pub reason: CorrectionReason,
/// Vocabulary the `reason` slug resolves against. Omit to use the canonical idiolect default.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason_vocab: Option<crate::generated::dev::idiolect::defs::VocabRef>,
pub visibility: crate::generated::dev::idiolect::defs::Visibility,
}
impl crate::Record for Correction {
const NSID: &'static str = "dev.idiolect.correction";
}
/// CorrectionReason. Open-enum slug; known values are kebab-cased; community-extended values pass through as `Other(String)`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CorrectionReason {
LensError,
DomainDifference,
SourceError,
DownstreamIdiosyncrasy,
UserMistake,
Retrospective,
/// Community-extended slug not present in the lexicon's
/// `knownValues`. Resolves through the sibling
/// `*Vocab` field on the containing record.
Other(String),
}
impl CorrectionReason {
/// Wire-form slug for this value. Known variants render
/// kebab-case; the fallback variant passes through verbatim.
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::LensError => "lens-error",
Self::DomainDifference => "domain-difference",
Self::SourceError => "source-error",
Self::DownstreamIdiosyncrasy => "downstream-idiosyncrasy",
Self::UserMistake => "user-mistake",
Self::Retrospective => "retrospective",
Self::Other(s) => s.as_str(),
}
}
/// Whether this slug is subsumed by `ancestor` under the
/// `subsumed_by` relation in the supplied vocab. Reflexive:
/// every slug is subsumed by itself.
#[must_use]
pub fn is_subsumed_by(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
ancestor: &str,
) -> bool {
vocab.is_subsumed_by(self.as_str(), ancestor)
}
/// Whether this slug satisfies a requirement of `target`
/// under the named `relation` in the supplied vocab.
/// Generalises `is_subsumed_by` to any directed relation
/// (e.g. `stronger_than`, `provides_at_least`,
/// `equivalent_to`). Reflexive: a slug satisfies itself.
#[must_use]
pub fn satisfies(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
relation: &str,
target: &str,
) -> bool {
if self.as_str() == target {
return true;
}
vocab
.walk_relation(self.as_str(), relation, false)
.iter()
.any(|n| n == target)
}
/// Translate this slug across vocabularies via
/// `equivalent_to` edges. Returns the translated slug as
/// a target enum value when a translation exists, `None`
/// when no path is found (callers fall back to passing
/// the slug through verbatim, which is wire-compatible).
#[must_use]
pub fn translate_to<T: From<String>>(
&self,
src_vocab_uri: &str,
tgt_vocab_uri: &str,
registry: &idiolect_records::vocab::VocabRegistry,
) -> Option<T> {
registry
.translate(src_vocab_uri, tgt_vocab_uri, self.as_str())
.map(T::from)
}
}
impl From<String> for CorrectionReason {
fn from(s: String) -> Self {
match s.as_str() {
"lens-error" => Self::LensError,
"domain-difference" => Self::DomainDifference,
"source-error" => Self::SourceError,
"downstream-idiosyncrasy" => Self::DownstreamIdiosyncrasy,
"user-mistake" => Self::UserMistake,
"retrospective" => Self::Retrospective,
_ => Self::Other(s),
}
}
}
impl From<&str> for CorrectionReason {
fn from(s: &str) -> Self {
match s {
"lens-error" => Self::LensError,
"domain-difference" => Self::DomainDifference,
"source-error" => Self::SourceError,
"downstream-idiosyncrasy" => Self::DownstreamIdiosyncrasy,
"user-mistake" => Self::UserMistake,
"retrospective" => Self::Retrospective,
_ => Self::Other(s.to_owned()),
}
}
}
impl serde::Serialize for CorrectionReason {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> serde::Deserialize<'de> for CorrectionReason {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(Self::from(s))
}
}