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