agentic_workflow/format/
reader.rs1use std::io::Read;
2
3use crate::types::{Workflow, WorkflowError, WorkflowResult};
4
5use super::writer::{AWF_MAGIC, AWF_VERSION, SectionType};
6
7pub struct AwfReader<R: Read> {
9 reader: R,
10 version: u32,
11 workflow_count: u32,
12 execution_count: u32,
13}
14
15impl<R: Read> AwfReader<R> {
16 pub fn new(reader: R) -> Self {
17 Self {
18 reader,
19 version: 0,
20 workflow_count: 0,
21 execution_count: 0,
22 }
23 }
24
25 pub fn read_header(&mut self) -> WorkflowResult<()> {
27 let mut magic = [0u8; 4];
28 self.reader.read_exact(&mut magic)?;
29
30 if &magic != AWF_MAGIC {
31 return Err(WorkflowError::FormatError(
32 "Invalid .awf file: bad magic bytes".to_string(),
33 ));
34 }
35
36 let mut version_bytes = [0u8; 4];
37 self.reader.read_exact(&mut version_bytes)?;
38 self.version = u32::from_le_bytes(version_bytes);
39
40 if self.version > AWF_VERSION {
41 return Err(WorkflowError::FormatError(format!(
42 "Unsupported .awf version: {} (max: {})",
43 self.version, AWF_VERSION
44 )));
45 }
46
47 let mut wf_count = [0u8; 4];
48 self.reader.read_exact(&mut wf_count)?;
49 self.workflow_count = u32::from_le_bytes(wf_count);
50
51 let mut exec_count = [0u8; 4];
52 self.reader.read_exact(&mut exec_count)?;
53 self.execution_count = u32::from_le_bytes(exec_count);
54
55 Ok(())
56 }
57
58 pub fn read_section_header(&mut self) -> WorkflowResult<(u8, u32)> {
60 let mut section_type = [0u8; 1];
61 self.reader.read_exact(&mut section_type)?;
62
63 let mut data_len = [0u8; 4];
64 self.reader.read_exact(&mut data_len)?;
65
66 Ok((section_type[0], u32::from_le_bytes(data_len)))
67 }
68
69 pub fn read_section_data(&mut self, data_len: u32) -> WorkflowResult<Vec<u8>> {
71 let mut data = vec![0u8; data_len as usize];
72 self.reader.read_exact(&mut data)?;
73
74 let mut stored_checksum = [0u8; 32];
75 self.reader.read_exact(&mut stored_checksum)?;
76
77 let computed_checksum = blake3::hash(&data);
78 if computed_checksum.as_bytes() != &stored_checksum {
79 return Err(WorkflowError::FormatError(
80 "Section checksum mismatch — data corrupted".to_string(),
81 ));
82 }
83
84 Ok(data)
85 }
86
87 pub fn read_workflow(&mut self) -> WorkflowResult<Workflow> {
89 let (section_type, data_len) = self.read_section_header()?;
90
91 if section_type != SectionType::WorkflowRegistry as u8 {
92 return Err(WorkflowError::FormatError(format!(
93 "Expected WorkflowRegistry section, got type {}",
94 section_type
95 )));
96 }
97
98 let data = self.read_section_data(data_len)?;
99 let workflow: Workflow = serde_json::from_slice(&data)
100 .map_err(|e| WorkflowError::SerializationError(e.to_string()))?;
101
102 Ok(workflow)
103 }
104
105 pub fn read_json_section(&mut self) -> WorkflowResult<(u8, serde_json::Value)> {
107 let (section_type, data_len) = self.read_section_header()?;
108 let data = self.read_section_data(data_len)?;
109
110 let value: serde_json::Value = serde_json::from_slice(&data)
111 .map_err(|e| WorkflowError::SerializationError(e.to_string()))?;
112
113 Ok((section_type, value))
114 }
115
116 pub fn version(&self) -> u32 {
118 self.version
119 }
120
121 pub fn workflow_count(&self) -> u32 {
123 self.workflow_count
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use crate::format::AwfWriter;
131 use std::io::Cursor;
132
133 #[test]
134 fn test_roundtrip() {
135 let mut buf = Vec::new();
137 {
138 let mut writer = AwfWriter::new(&mut buf);
139 writer.write_header().unwrap();
140
141 let wf = Workflow::new("roundtrip", "Test roundtrip");
142 writer.write_workflow(&wf).unwrap();
143 writer.finish().unwrap();
144 }
145
146 let cursor = Cursor::new(buf);
148 let mut reader = AwfReader::new(cursor);
149 reader.read_header().unwrap();
150
151 let wf = reader.read_workflow().unwrap();
152 assert_eq!(wf.name, "roundtrip");
153 }
154}