Skip to main content

agentic_workflow/format/
reader.rs

1use std::io::Read;
2
3use crate::types::{Workflow, WorkflowError, WorkflowResult};
4
5use super::writer::{AWF_MAGIC, AWF_VERSION, SectionType};
6
7/// Reads .awf binary files.
8pub 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    /// Read and validate the file header.
26    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    /// Read a section header.
59    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    /// Read section data and verify BLAKE3 checksum.
70    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    /// Read a workflow from section data.
88    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    /// Read a JSON section.
106    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    /// Get file version.
117    pub fn version(&self) -> u32 {
118        self.version
119    }
120
121    /// Get workflow count from header.
122    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        // Write
136        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        // Read
147        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}