Skip to main content

agentic_workflow/format/
writer.rs

1use std::io::Write;
2
3use crate::types::{Workflow, WorkflowError, WorkflowResult};
4
5/// Magic bytes for .awf file format.
6pub const AWF_MAGIC: &[u8; 4] = b"AWFL";
7/// Current format version.
8pub const AWF_VERSION: u32 = 1;
9
10/// Section types in the .awf file.
11#[repr(u8)]
12#[derive(Debug, Clone, Copy)]
13pub enum SectionType {
14    WorkflowRegistry = 1,
15    TemplateLibrary = 2,
16    ExecutionHistory = 3,
17    ScheduleTable = 4,
18    StateMachineTable = 5,
19    TriggerIndex = 6,
20    AuditLog = 7,
21    IdempotencyCache = 8,
22    VariableStore = 9,
23}
24
25/// Writes .awf binary files.
26pub struct AwfWriter<W: Write> {
27    writer: W,
28    workflow_count: u32,
29    execution_count: u32,
30}
31
32impl<W: Write> AwfWriter<W> {
33    pub fn new(writer: W) -> Self {
34        Self {
35            writer,
36            workflow_count: 0,
37            execution_count: 0,
38        }
39    }
40
41    /// Write the file header.
42    pub fn write_header(&mut self) -> WorkflowResult<()> {
43        // Magic
44        self.writer.write_all(AWF_MAGIC)?;
45        // Version (little-endian)
46        self.writer.write_all(&AWF_VERSION.to_le_bytes())?;
47        // Workflow count (placeholder — patched at end)
48        self.writer.write_all(&0u32.to_le_bytes())?;
49        // Execution count (placeholder — patched at end)
50        self.writer.write_all(&0u32.to_le_bytes())?;
51
52        Ok(())
53    }
54
55    /// Write a section header.
56    pub fn write_section_header(
57        &mut self,
58        section_type: SectionType,
59        data_len: u32,
60    ) -> WorkflowResult<()> {
61        self.writer.write_all(&[section_type as u8])?;
62        self.writer.write_all(&data_len.to_le_bytes())?;
63        Ok(())
64    }
65
66    /// Write a workflow to the registry section.
67    pub fn write_workflow(&mut self, workflow: &Workflow) -> WorkflowResult<()> {
68        let json = serde_json::to_vec(workflow)
69            .map_err(|e| WorkflowError::SerializationError(e.to_string()))?;
70
71        let checksum = blake3::hash(&json);
72
73        self.write_section_header(SectionType::WorkflowRegistry, json.len() as u32)?;
74        self.writer.write_all(&json)?;
75        self.writer.write_all(checksum.as_bytes())?;
76
77        self.workflow_count += 1;
78        Ok(())
79    }
80
81    /// Write raw JSON data as a section.
82    pub fn write_json_section(
83        &mut self,
84        section_type: SectionType,
85        data: &serde_json::Value,
86    ) -> WorkflowResult<()> {
87        let json = serde_json::to_vec(data)
88            .map_err(|e| WorkflowError::SerializationError(e.to_string()))?;
89
90        let checksum = blake3::hash(&json);
91
92        self.write_section_header(section_type, json.len() as u32)?;
93        self.writer.write_all(&json)?;
94        self.writer.write_all(checksum.as_bytes())?;
95
96        Ok(())
97    }
98
99    /// Finish writing and flush.
100    pub fn finish(mut self) -> WorkflowResult<W> {
101        self.writer.flush()?;
102        Ok(self.writer)
103    }
104
105    /// Get counts.
106    pub fn workflow_count(&self) -> u32 {
107        self.workflow_count
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn test_write_header() {
117        let mut buf = Vec::new();
118        let mut writer = AwfWriter::new(&mut buf);
119        writer.write_header().unwrap();
120        assert_eq!(&buf[0..4], AWF_MAGIC);
121    }
122
123    #[test]
124    fn test_write_workflow() {
125        let mut buf = Vec::new();
126        {
127            let mut writer = AwfWriter::new(&mut buf);
128            writer.write_header().unwrap();
129
130            let wf = Workflow::new("test", "A test workflow");
131            writer.write_workflow(&wf).unwrap();
132            assert_eq!(writer.workflow_count(), 1);
133            writer.finish().unwrap();
134        }
135
136        assert!(buf.len() > 16);
137    }
138}