1use super::{
4 SectionType,
5 schema::{
6 ContextSection, DrvHeader, PersonaSection, RuleCategory, RuleEntry, StandardsSection,
7 WorkflowSection, WorkflowStep,
8 },
9};
10use crate::{DrivenError, Result, parser::UnifiedRule};
11use bytes::Buf;
12use std::io::Cursor;
13
14#[derive(Debug)]
16pub struct DrvDecoder<'a> {
17 data: &'a [u8],
19 string_table: Vec<&'a str>,
21 header: DrvHeader,
23}
24
25impl<'a> DrvDecoder<'a> {
26 pub fn new(data: &'a [u8]) -> Result<Self> {
28 if data.len() < 16 {
29 return Err(DrivenError::InvalidBinary(
30 "Data too short for header".to_string(),
31 ));
32 }
33
34 let header: DrvHeader = *bytemuck::from_bytes(&data[..16]);
36 header.validate()?;
37
38 let stored_checksum = header.checksum;
40 let computed = {
41 let hash = blake3::hash(&data[16..]);
42 let bytes = hash.as_bytes();
43 u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
44 };
45
46 if stored_checksum != computed {
47 return Err(DrivenError::InvalidBinary(format!(
48 "Checksum mismatch: stored={:08x}, computed={:08x}",
49 stored_checksum, computed
50 )));
51 }
52
53 let mut decoder = Self {
54 data,
55 string_table: Vec::new(),
56 header,
57 };
58
59 decoder.parse_string_table()?;
61
62 Ok(decoder)
63 }
64
65 fn parse_string_table(&mut self) -> Result<()> {
67 let mut cursor = Cursor::new(&self.data[16..]);
68
69 if cursor.remaining() < 1 {
71 return Err(DrivenError::InvalidBinary(
72 "Missing string table section".to_string(),
73 ));
74 }
75
76 let section_type = cursor.get_u8();
77 if section_type != SectionType::StringTable as u8 {
78 return Err(DrivenError::InvalidBinary(format!(
79 "Expected string table (0x01), got 0x{:02x}",
80 section_type
81 )));
82 }
83
84 if cursor.remaining() < 4 {
86 return Err(DrivenError::InvalidBinary(
87 "Missing string table count".to_string(),
88 ));
89 }
90 let count = cursor.get_u32_le() as usize;
91
92 self.string_table.reserve(count);
94 let base_offset = 16 + 5; let mut offset = base_offset;
96
97 for _ in 0..count {
98 if offset + 2 > self.data.len() {
99 return Err(DrivenError::InvalidBinary(
100 "Unexpected end of string table".to_string(),
101 ));
102 }
103
104 let len = u16::from_le_bytes([self.data[offset], self.data[offset + 1]]) as usize;
105 offset += 2;
106
107 if offset + len > self.data.len() {
108 return Err(DrivenError::InvalidBinary(
109 "String extends past end of data".to_string(),
110 ));
111 }
112
113 let s = std::str::from_utf8(&self.data[offset..offset + len])
114 .map_err(|e| DrivenError::InvalidBinary(format!("Invalid UTF-8: {}", e)))?;
115 self.string_table.push(s);
116 offset += len;
117 }
118
119 Ok(())
120 }
121
122 pub fn get_string(&self, idx: u32) -> Result<&'a str> {
124 self.string_table
125 .get(idx as usize)
126 .copied()
127 .ok_or_else(|| DrivenError::InvalidBinary(format!("Invalid string index: {}", idx)))
128 }
129
130 pub fn decode_all(&self) -> Result<Vec<UnifiedRule>> {
132 let mut rules = Vec::new();
133 let mut offset = 16 + 5; for s in &self.string_table {
137 offset += 2 + s.len();
138 }
139
140 while offset < self.data.len() {
142 let section_type = SectionType::try_from(self.data[offset])?;
143 offset += 1;
144
145 match section_type {
146 SectionType::StringTable => {
147 unreachable!("String table should be first");
149 }
150 SectionType::Persona => {
151 let (persona, new_offset) = self.decode_persona(offset)?;
152 offset = new_offset;
153 rules.push(UnifiedRule::Persona {
154 name: self.get_string(persona.name_idx)?.to_string(),
155 role: self.get_string(persona.role_idx)?.to_string(),
156 identity: if persona.identity_idx > 0 {
157 Some(self.get_string(persona.identity_idx)?.to_string())
158 } else {
159 None
160 },
161 style: if persona.style_idx > 0 {
162 Some(self.get_string(persona.style_idx)?.to_string())
163 } else {
164 None
165 },
166 traits: persona
167 .traits
168 .iter()
169 .map(|&idx| self.get_string(idx).map(String::from))
170 .collect::<Result<Vec<_>>>()?,
171 principles: persona
172 .principles
173 .iter()
174 .map(|&idx| self.get_string(idx).map(String::from))
175 .collect::<Result<Vec<_>>>()?,
176 });
177 }
178 SectionType::Standards => {
179 let (standards, new_offset) = self.decode_standards(offset)?;
180 offset = new_offset;
181 for rule in standards.rules {
182 rules.push(UnifiedRule::Standard {
183 category: rule.category,
184 priority: rule.priority,
185 description: self.get_string(rule.description_idx)?.to_string(),
186 pattern: if rule.pattern_idx > 0 {
187 Some(self.get_string(rule.pattern_idx)?.to_string())
188 } else {
189 None
190 },
191 });
192 }
193 }
194 SectionType::Context => {
195 let (context, new_offset) = self.decode_context(offset)?;
196 offset = new_offset;
197 rules.push(UnifiedRule::Context {
198 includes: context
199 .include_patterns
200 .iter()
201 .map(|&idx| self.get_string(idx).map(String::from))
202 .collect::<Result<Vec<_>>>()?,
203 excludes: context
204 .exclude_patterns
205 .iter()
206 .map(|&idx| self.get_string(idx).map(String::from))
207 .collect::<Result<Vec<_>>>()?,
208 focus: context
209 .focus_areas
210 .iter()
211 .map(|&idx| self.get_string(idx).map(String::from))
212 .collect::<Result<Vec<_>>>()?,
213 });
214 }
215 SectionType::Workflow => {
216 let (workflow, new_offset) = self.decode_workflow(offset)?;
217 offset = new_offset;
218 let steps = workflow
219 .steps
220 .iter()
221 .map(|step| {
222 Ok(crate::parser::WorkflowStepData {
223 name: self.get_string(step.name_idx)?.to_string(),
224 description: self.get_string(step.description_idx)?.to_string(),
225 condition: if step.condition_idx > 0 {
226 Some(self.get_string(step.condition_idx)?.to_string())
227 } else {
228 None
229 },
230 actions: step
231 .actions
232 .iter()
233 .map(|&idx| self.get_string(idx).map(String::from))
234 .collect::<Result<Vec<_>>>()?,
235 })
236 })
237 .collect::<Result<Vec<_>>>()?;
238 rules.push(UnifiedRule::Workflow {
239 name: self.get_string(workflow.name_idx)?.to_string(),
240 steps,
241 });
242 }
243 }
244 }
245
246 Ok(rules)
247 }
248
249 fn decode_persona(&self, offset: usize) -> Result<(PersonaSection, usize)> {
250 let mut cursor = Cursor::new(&self.data[offset..]);
251
252 let name_idx = cursor.get_u32_le();
253 let role_idx = cursor.get_u32_le();
254 let identity_idx = cursor.get_u32_le();
255 let style_idx = cursor.get_u32_le();
256
257 let traits_count = cursor.get_u16_le() as usize;
258 let mut traits = Vec::with_capacity(traits_count);
259 for _ in 0..traits_count {
260 traits.push(cursor.get_u32_le());
261 }
262
263 let principles_count = cursor.get_u16_le() as usize;
264 let mut principles = Vec::with_capacity(principles_count);
265 for _ in 0..principles_count {
266 principles.push(cursor.get_u32_le());
267 }
268
269 let persona = PersonaSection {
270 name_idx,
271 role_idx,
272 identity_idx,
273 style_idx,
274 traits,
275 principles,
276 };
277
278 Ok((persona, offset + cursor.position() as usize))
279 }
280
281 fn decode_standards(&self, offset: usize) -> Result<(StandardsSection, usize)> {
282 let mut cursor = Cursor::new(&self.data[offset..]);
283
284 let count = cursor.get_u32_le() as usize;
285 let mut rules = Vec::with_capacity(count);
286
287 for _ in 0..count {
288 let category = RuleCategory::from(cursor.get_u8());
289 let priority = cursor.get_u8();
290 let description_idx = cursor.get_u32_le();
291 let pattern_idx = cursor.get_u32_le();
292
293 rules.push(RuleEntry {
294 category,
295 priority,
296 description_idx,
297 pattern_idx,
298 });
299 }
300
301 Ok((
302 StandardsSection { rules },
303 offset + cursor.position() as usize,
304 ))
305 }
306
307 fn decode_context(&self, offset: usize) -> Result<(ContextSection, usize)> {
308 let mut cursor = Cursor::new(&self.data[offset..]);
309
310 let include_count = cursor.get_u16_le() as usize;
311 let mut include_patterns = Vec::with_capacity(include_count);
312 for _ in 0..include_count {
313 include_patterns.push(cursor.get_u32_le());
314 }
315
316 let exclude_count = cursor.get_u16_le() as usize;
317 let mut exclude_patterns = Vec::with_capacity(exclude_count);
318 for _ in 0..exclude_count {
319 exclude_patterns.push(cursor.get_u32_le());
320 }
321
322 let focus_count = cursor.get_u16_le() as usize;
323 let mut focus_areas = Vec::with_capacity(focus_count);
324 for _ in 0..focus_count {
325 focus_areas.push(cursor.get_u32_le());
326 }
327
328 let dep_count = cursor.get_u16_le() as usize;
329 let mut dependencies = Vec::with_capacity(dep_count);
330 for _ in 0..dep_count {
331 let name = cursor.get_u32_le();
332 let version = cursor.get_u32_le();
333 dependencies.push((name, version));
334 }
335
336 Ok((
337 ContextSection {
338 include_patterns,
339 exclude_patterns,
340 focus_areas,
341 dependencies,
342 },
343 offset + cursor.position() as usize,
344 ))
345 }
346
347 fn decode_workflow(&self, offset: usize) -> Result<(WorkflowSection, usize)> {
348 let mut cursor = Cursor::new(&self.data[offset..]);
349
350 let name_idx = cursor.get_u32_le();
351 let step_count = cursor.get_u16_le() as usize;
352
353 let mut steps = Vec::with_capacity(step_count);
354 for _ in 0..step_count {
355 let step_name_idx = cursor.get_u32_le();
356 let description_idx = cursor.get_u32_le();
357 let condition_idx = cursor.get_u32_le();
358
359 let action_count = cursor.get_u16_le() as usize;
360 let mut actions = Vec::with_capacity(action_count);
361 for _ in 0..action_count {
362 actions.push(cursor.get_u32_le());
363 }
364
365 steps.push(WorkflowStep {
366 name_idx: step_name_idx,
367 description_idx,
368 condition_idx,
369 actions,
370 });
371 }
372
373 Ok((
374 WorkflowSection { name_idx, steps },
375 offset + cursor.position() as usize,
376 ))
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383 use crate::format::DrvEncoder;
384
385 #[test]
386 fn test_roundtrip_empty() {
387 let encoder = DrvEncoder::new();
388 let data = encoder.encode(&[]).unwrap();
389
390 let decoder = DrvDecoder::new(&data).unwrap();
391 let rules = decoder.decode_all().unwrap();
392
393 assert!(rules.is_empty());
394 }
395
396 #[test]
397 fn test_invalid_magic() {
398 let mut data = vec![0u8; 20];
399 data[0..4].copy_from_slice(b"BAD\0");
400
401 let result = DrvDecoder::new(&data);
402 assert!(result.is_err());
403 }
404
405 #[test]
406 fn test_too_short() {
407 let data = vec![0u8; 10];
408 let result = DrvDecoder::new(&data);
409 assert!(result.is_err());
410 }
411}