entrouter_universal/
universal_struct.rs1use crate::{decode_str, encode_str, fingerprint_str, UniversalError};
19use serde::{Deserialize, Serialize};
20use std::collections::HashMap;
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct WrappedField {
25 pub name: String,
27 pub d: String,
29 pub f: String,
31}
32
33impl WrappedField {
34 pub fn wrap(name: &str, value: &str) -> Self {
36 Self {
37 name: name.to_string(),
38 d: encode_str(value),
39 f: fingerprint_str(value),
40 }
41 }
42
43 pub fn verify(&self) -> Result<String, UniversalError> {
45 let decoded = decode_str(&self.d)?;
46 let actual_fp = fingerprint_str(&decoded);
47 if actual_fp != self.f {
48 return Err(UniversalError::IntegrityViolation {
49 expected: self.f.clone(),
50 actual: actual_fp,
51 });
52 }
53 Ok(decoded)
54 }
55
56 pub fn is_intact(&self) -> bool {
58 self.verify().is_ok()
59 }
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct UniversalStruct {
69 pub fields: Vec<WrappedField>,
70}
71
72#[derive(Debug, Clone, PartialEq)]
74pub struct FieldVerifyResult {
75 pub name: String,
77 pub intact: bool,
79 pub value: Option<String>,
81 pub error: Option<String>,
83}
84
85impl std::fmt::Display for FieldVerifyResult {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 if self.intact {
88 write!(f, "{}: Intact", self.name)
89 } else {
90 write!(
91 f,
92 "{}: Violated ({})",
93 self.name,
94 self.error.as_deref().unwrap_or("unknown")
95 )
96 }
97 }
98}
99
100#[derive(Debug, Clone, PartialEq)]
102pub struct StructVerifyResult {
103 pub all_intact: bool,
105 pub fields: Vec<FieldVerifyResult>,
107 pub violations: Vec<String>,
109}
110
111impl std::fmt::Display for StructVerifyResult {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 if self.all_intact {
114 write!(f, "All {} fields intact", self.fields.len())
115 } else {
116 write!(f, "Violations in: {}", self.violations.join(", "))
117 }
118 }
119}
120
121impl UniversalStruct {
122 #[must_use]
124 pub fn wrap_fields(fields: &[(&str, &str)]) -> Self {
125 Self {
126 fields: fields
127 .iter()
128 .map(|(name, value)| WrappedField::wrap(name, value))
129 .collect(),
130 }
131 }
132
133 pub fn verify_all(&self) -> StructVerifyResult {
135 let mut all_intact = true;
136 let mut violations = Vec::new();
137 let fields = self
138 .fields
139 .iter()
140 .map(|f| match f.verify() {
141 Ok(value) => FieldVerifyResult {
142 name: f.name.clone(),
143 intact: true,
144 value: Some(value),
145 error: None,
146 },
147 Err(e) => {
148 all_intact = false;
149 violations.push(f.name.clone());
150 FieldVerifyResult {
151 name: f.name.clone(),
152 intact: false,
153 value: None,
154 error: Some(e.to_string()),
155 }
156 }
157 })
158 .collect();
159
160 StructVerifyResult {
161 all_intact,
162 fields,
163 violations,
164 }
165 }
166
167 pub fn get(&self, name: &str) -> Result<String, UniversalError> {
169 self.fields
170 .iter()
171 .find(|f| f.name == name)
172 .ok_or_else(|| {
173 UniversalError::MalformedEnvelope(format!("field '{}' not found", name))
174 })?
175 .verify()
176 }
177
178 pub fn to_map(&self) -> Result<HashMap<String, String>, UniversalError> {
180 let result = self.verify_all();
181 if !result.all_intact {
182 return Err(UniversalError::IntegrityViolation {
183 expected: "all fields intact".to_string(),
184 actual: format!("violations in: {}", result.violations.join(", ")),
185 });
186 }
187 Ok(result
188 .fields
189 .into_iter()
190 .filter_map(|f| f.value.map(|v| (f.name, v)))
191 .collect())
192 }
193
194 pub fn assert_intact(&self) {
196 let result = self.verify_all();
197 if !result.all_intact {
198 panic!(
199 "Entrouter Universal: field integrity violations in: {}",
200 result.violations.join(", ")
201 );
202 }
203 }
204
205 pub fn report(&self) -> String {
207 let result = self.verify_all();
208 let mut out = String::new();
209 out.push_str("━━━━ Entrouter Universal Field Report ━━━━\n");
210 out.push_str(&format!("All intact: {}\n\n", result.all_intact));
211 for field in &result.fields {
212 let status = if field.intact { "✅" } else { "❌ VIOLATED" };
213 out.push_str(&format!(
214 " {}: {} - {}\n",
215 field.name,
216 status,
217 field.value.as_deref().unwrap_or("-")
218 ));
219 if let Some(err) = &field.error {
220 out.push_str(&format!(" Error: {}\n", err));
221 }
222 }
223 out.push_str("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
224 out
225 }
226
227 pub fn to_json(&self) -> Result<String, UniversalError> {
229 serde_json::to_string(self).map_err(|e| UniversalError::SerializationError(e.to_string()))
230 }
231
232 pub fn from_json(s: &str) -> Result<Self, UniversalError> {
234 serde_json::from_str(s).map_err(|e| UniversalError::SerializationError(e.to_string()))
235 }
236}