Skip to main content

crdt_graph/flatbuffers/
string.rs

1use crate::graph::UpdateOperation;
2use crate::types::string;
3use flatbuffers::FlatBufferBuilder;
4
5use super::crdt_graph_with_str_data_generated::crdt_graph::fb_str;
6use super::{uuid_from_fb, DecodeError};
7
8// ---------------------------------------------------------------------------
9// Encoding
10// ---------------------------------------------------------------------------
11
12/// Encodes a single [`string::Operation`] into a FlatBuffer byte vector (file identifier `"CRD3"`).
13pub fn encode_operation(op: &string::Operation) -> Vec<u8> {
14    let mut builder = FlatBufferBuilder::new();
15    let op_offset = write_update_operation(&mut builder, op);
16
17    let ops_vec = builder.create_vector(&[op_offset]);
18    let log = fb_str::OperationLog::create(
19        &mut builder,
20        &fb_str::OperationLogArgs {
21            operations: Some(ops_vec),
22        },
23    );
24    builder.finish(log, Some("CRD3"));
25    builder.finished_data().to_vec()
26}
27
28/// Encodes multiple operations into a single FlatBuffer byte vector.
29pub fn encode_operation_log(ops: &[string::Operation]) -> Vec<u8> {
30    let mut builder = FlatBufferBuilder::new();
31
32    let offsets: Vec<_> = ops
33        .iter()
34        .map(|op| write_update_operation(&mut builder, op))
35        .collect();
36
37    let ops_vec = builder.create_vector(&offsets);
38    let log = fb_str::OperationLog::create(
39        &mut builder,
40        &fb_str::OperationLogArgs {
41            operations: Some(ops_vec),
42        },
43    );
44    builder.finish(log, Some("CRD3"));
45    builder.finished_data().to_vec()
46}
47
48fn write_update_operation<'bldr, A: flatbuffers::Allocator + 'bldr>(
49    builder: &mut FlatBufferBuilder<'bldr, A>,
50    op: &string::Operation,
51) -> flatbuffers::WIPOffset<fb_str::UpdateOperation<'bldr>> {
52    match op {
53        UpdateOperation::AddVertex(v) => {
54            let id = fb_str::Uuid(*v.id.as_bytes());
55            let data = v.data.as_ref().map(|d| builder.create_string(d));
56            let inner = fb_str::AddVertex::create(
57                builder,
58                &fb_str::AddVertexArgs {
59                    id: Some(&id),
60                    data,
61                },
62            );
63            fb_str::UpdateOperation::create(
64                builder,
65                &fb_str::UpdateOperationArgs {
66                    operation_type: fb_str::Operation::AddVertex,
67                    operation: Some(inner.as_union_value()),
68                },
69            )
70        }
71        UpdateOperation::RemoveVertex(v) => {
72            let id = fb_str::Uuid(*v.id.as_bytes());
73            let add_vertex_id = fb_str::Uuid(*v.add_vertex_id.as_bytes());
74            let inner = fb_str::RemoveVertex::create(
75                builder,
76                &fb_str::RemoveVertexArgs {
77                    id: Some(&id),
78                    add_vertex_id: Some(&add_vertex_id),
79                },
80            );
81            fb_str::UpdateOperation::create(
82                builder,
83                &fb_str::UpdateOperationArgs {
84                    operation_type: fb_str::Operation::RemoveVertex,
85                    operation: Some(inner.as_union_value()),
86                },
87            )
88        }
89        UpdateOperation::AddEdge(e) => {
90            let id = fb_str::Uuid(*e.id.as_bytes());
91            let source = fb_str::Uuid(*e.source.as_bytes());
92            let target = fb_str::Uuid(*e.target.as_bytes());
93            let data = e.data.as_ref().map(|d| builder.create_string(d));
94            let inner = fb_str::AddEdge::create(
95                builder,
96                &fb_str::AddEdgeArgs {
97                    id: Some(&id),
98                    source: Some(&source),
99                    target: Some(&target),
100                    data,
101                },
102            );
103            fb_str::UpdateOperation::create(
104                builder,
105                &fb_str::UpdateOperationArgs {
106                    operation_type: fb_str::Operation::AddEdge,
107                    operation: Some(inner.as_union_value()),
108                },
109            )
110        }
111        UpdateOperation::RemoveEdge(e) => {
112            let id = fb_str::Uuid(*e.id.as_bytes());
113            let add_edge_id = fb_str::Uuid(*e.add_edge_id.as_bytes());
114            let inner = fb_str::RemoveEdge::create(
115                builder,
116                &fb_str::RemoveEdgeArgs {
117                    id: Some(&id),
118                    add_edge_id: Some(&add_edge_id),
119                },
120            );
121            fb_str::UpdateOperation::create(
122                builder,
123                &fb_str::UpdateOperationArgs {
124                    operation_type: fb_str::Operation::RemoveEdge,
125                    operation: Some(inner.as_union_value()),
126                },
127            )
128        }
129    }
130}
131
132// ---------------------------------------------------------------------------
133// Decoding
134// ---------------------------------------------------------------------------
135
136/// Decodes a single operation from a string-data FlatBuffer.
137pub fn decode_operation(buf: &[u8]) -> Result<string::Operation, DecodeError> {
138    let ops = decode_operation_log(buf)?;
139    ops.into_iter()
140        .next()
141        .ok_or(DecodeError::UnknownOperationType)
142}
143
144/// Decodes all operations from a string-data FlatBuffer `OperationLog`.
145pub fn decode_operation_log(buf: &[u8]) -> Result<Vec<string::Operation>, DecodeError> {
146    let log = fb_str::root_as_operation_log(buf)?;
147    let operations = log.operations();
148
149    let mut result = Vec::with_capacity(operations.len());
150    for fb_op in operations.iter() {
151        let op = read_update_operation(&fb_op)?;
152        result.push(op);
153    }
154    Ok(result)
155}
156
157fn read_update_operation(
158    fb_op: &fb_str::UpdateOperation<'_>,
159) -> Result<string::Operation, DecodeError> {
160    match fb_op.operation_type() {
161        fb_str::Operation::AddVertex => {
162            let v = fb_op
163                .operation_as_add_vertex()
164                .ok_or(DecodeError::UnknownOperationType)?;
165            Ok(UpdateOperation::AddVertex(string::AddVertex {
166                id: uuid_from_fb(&v.id().0),
167                data: v.data().map(|s| s.to_owned()),
168            }))
169        }
170        fb_str::Operation::RemoveVertex => {
171            let v = fb_op
172                .operation_as_remove_vertex()
173                .ok_or(DecodeError::UnknownOperationType)?;
174            Ok(UpdateOperation::RemoveVertex(crate::types::RemoveVertex {
175                id: uuid_from_fb(&v.id().0),
176                add_vertex_id: uuid_from_fb(&v.add_vertex_id().0),
177            }))
178        }
179        fb_str::Operation::AddEdge => {
180            let e = fb_op
181                .operation_as_add_edge()
182                .ok_or(DecodeError::UnknownOperationType)?;
183            Ok(UpdateOperation::AddEdge(string::AddEdge {
184                id: uuid_from_fb(&e.id().0),
185                source: uuid_from_fb(&e.source().0),
186                target: uuid_from_fb(&e.target().0),
187                data: e.data().map(|s| s.to_owned()),
188            }))
189        }
190        fb_str::Operation::RemoveEdge => {
191            let e = fb_op
192                .operation_as_remove_edge()
193                .ok_or(DecodeError::UnknownOperationType)?;
194            Ok(UpdateOperation::RemoveEdge(crate::types::RemoveEdge {
195                id: uuid_from_fb(&e.id().0),
196                add_edge_id: uuid_from_fb(&e.add_edge_id().0),
197            }))
198        }
199        _ => Err(DecodeError::UnknownOperationType),
200    }
201}