1use crate::ast::{EdgeType, NodeId, PdfAstGraph, PdfDocument};
2use crate::types::PdfValue;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6const AST_SCHEMA_VERSION: &str = "1.1";
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct SerializableDocument {
10 pub version: String,
11 pub schema_version: String,
12 pub ast: SerializableGraph,
13 pub catalog: Option<usize>,
14 pub info: Option<usize>,
15 pub trailer: SerializableValue,
16 pub xref_entries: HashMap<String, SerializableXRefEntry>,
17 pub metadata: SerializableDocumentMetadata,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct SerializableXRefEntry {
22 pub offset: Option<u64>,
23 pub generation: u16,
24 pub entry_type: String,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct SerializableDocumentMetadata {
29 pub file_size: Option<u64>,
30 pub linearized: bool,
31 pub encrypted: bool,
32 pub has_forms: bool,
33 pub has_xfa: bool,
34 pub xfa_packets: usize,
35 pub has_xfa_scripts: bool,
36 pub xfa_script_nodes: usize,
37 pub has_hybrid_forms: bool,
38 pub form_field_count: usize,
39 pub has_javascript: bool,
40 pub has_embedded_files: bool,
41 pub has_signatures: bool,
42 pub has_richmedia: bool,
43 pub richmedia_annotations: usize,
44 pub richmedia_assets: usize,
45 pub richmedia_scripts: usize,
46 pub has_3d: bool,
47 pub threed_annotations: usize,
48 pub threed_u3d: usize,
49 pub threed_prc: usize,
50 pub has_audio: bool,
51 pub audio_annotations: usize,
52 pub has_video: bool,
53 pub video_annotations: usize,
54 pub has_dss: bool,
55 pub dss_vri_count: usize,
56 pub dss_certs: usize,
57 pub dss_ocsp: usize,
58 pub dss_crl: usize,
59 pub dss_timestamps: usize,
60 pub page_count: usize,
61 pub producer: Option<String>,
62 pub creator: Option<String>,
63 pub creation_date: Option<String>,
64 pub modification_date: Option<String>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct SerializableGraph {
69 pub nodes: Vec<SerializableNode>,
70 pub edges: Vec<SerializableEdge>,
71 pub root: Option<usize>,
72 pub metadata: GraphMetadata,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct SerializableNode {
77 pub id: usize,
78 pub node_type: String,
79 pub value: SerializableValue,
80 pub object_id: Option<(u32, u16)>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct SerializableEdge {
85 pub from: usize,
86 pub to: usize,
87 pub edge_type: String,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct GraphMetadata {
92 pub node_count: usize,
93 pub edge_count: usize,
94 pub is_cyclic: bool,
95 pub serialization_version: String,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
99#[serde(tag = "type", content = "value")]
100pub enum SerializableValue {
101 Null,
102 Boolean(bool),
103 Integer(i64),
104 Real(f64),
105 String(String),
106 Name(String),
107 Array(Vec<SerializableValue>),
108 Dictionary(HashMap<String, SerializableValue>),
109 Stream {
110 dictionary: HashMap<String, SerializableValue>,
111 data: Vec<u8>,
112 lazy: Option<crate::types::StreamReference>,
113 },
114 Reference {
115 object_id: u32,
116 generation: u16,
117 },
118}
119
120impl SerializableGraph {
121 pub fn from_ast(ast: &PdfAstGraph) -> Self {
122 let serializer = GraphSerializer::new();
123 serializer.serialize(ast)
124 }
125
126 pub fn to_json(&self) -> serde_json::Result<String> {
127 serde_json::to_string_pretty(self)
128 }
129
130 pub fn to_cbor(&self) -> serde_cbor::Result<Vec<u8>> {
131 serde_cbor::to_vec(self)
132 }
133
134 pub fn from_json(json: &str) -> serde_json::Result<Self> {
135 serde_json::from_str(json)
136 }
137
138 pub fn from_cbor(data: &[u8]) -> serde_cbor::Result<Self> {
139 serde_cbor::from_slice(data)
140 }
141}
142
143struct GraphSerializer {
144 nodes: Vec<SerializableNode>,
145 edges: Vec<SerializableEdge>,
146 node_id_map: HashMap<NodeId, usize>,
147 next_serial_id: usize,
148}
149
150impl GraphSerializer {
151 fn new() -> Self {
152 Self {
153 nodes: Vec::new(),
154 edges: Vec::new(),
155 node_id_map: HashMap::new(),
156 next_serial_id: 0,
157 }
158 }
159
160 fn serialize(mut self, ast: &PdfAstGraph) -> SerializableGraph {
161 for node in ast.get_all_nodes() {
163 let serial_id = self.next_serial_id;
164 self.next_serial_id += 1;
165
166 self.node_id_map.insert(node.id, serial_id);
167
168 let object_id = match &node.node_type {
170 crate::ast::NodeType::Object(obj_id) => Some((obj_id.number, obj_id.generation)),
171 _ => None,
172 };
173
174 let serialized_node = SerializableNode {
175 id: serial_id,
176 node_type: format!("{:?}", node.node_type)
177 .split('(')
178 .next()
179 .unwrap_or("Unknown")
180 .to_string(),
181 value: Self::serialize_value(&node.value),
182 object_id,
183 };
184
185 self.nodes.push(serialized_node);
186 }
187
188 for edge in ast.get_all_edges() {
190 if let (Some(&from_id), Some(&to_id)) = (
191 self.node_id_map.get(&edge.from),
192 self.node_id_map.get(&edge.to),
193 ) {
194 let serialized_edge = SerializableEdge {
195 from: from_id,
196 to: to_id,
197 edge_type: format!("{:?}", edge.edge_type),
198 };
199 self.edges.push(serialized_edge);
200 }
201 }
202
203 let root_serial_id = ast
205 .root
206 .and_then(|root_node_id| self.node_id_map.get(&root_node_id).copied());
207
208 SerializableGraph {
209 nodes: self.nodes,
210 edges: self.edges,
211 root: root_serial_id,
212 metadata: GraphMetadata {
213 node_count: ast.node_count(),
214 edge_count: ast.edge_count(),
215 is_cyclic: ast.is_cyclic(),
216 serialization_version: "1.0".to_string(),
217 },
218 }
219 }
220
221 fn serialize_value(value: &PdfValue) -> SerializableValue {
222 match value {
223 PdfValue::Null => SerializableValue::Null,
224 PdfValue::Boolean(b) => SerializableValue::Boolean(*b),
225 PdfValue::Integer(i) => SerializableValue::Integer(*i),
226 PdfValue::Real(r) => SerializableValue::Real(*r),
227 PdfValue::String(s) => SerializableValue::String(s.to_string_lossy()),
228 PdfValue::Name(n) => SerializableValue::Name(n.as_str().to_string()),
229 PdfValue::Array(arr) => {
230 let items: Vec<SerializableValue> = arr.iter().map(Self::serialize_value).collect();
231 SerializableValue::Array(items)
232 }
233 PdfValue::Dictionary(dict) => {
234 let mut map = HashMap::new();
235 for (key, val) in dict.iter() {
236 map.insert(key.to_string(), Self::serialize_value(val));
237 }
238 SerializableValue::Dictionary(map)
239 }
240 PdfValue::Stream(stream) => {
241 let mut dict_map = HashMap::new();
242 for (key, val) in stream.dict.iter() {
243 dict_map.insert(key.to_string(), Self::serialize_value(val));
244 }
245 SerializableValue::Stream {
246 dictionary: dict_map,
247 data: match &stream.data {
248 crate::types::StreamData::Raw(bytes) => bytes.clone(),
249 crate::types::StreamData::Decoded(bytes) => bytes.clone(),
250 crate::types::StreamData::Lazy(_) => Vec::new(),
251 },
252 lazy: match &stream.data {
253 crate::types::StreamData::Lazy(reference) => Some(reference.clone()),
254 _ => None,
255 },
256 }
257 }
258 PdfValue::Reference(r) => SerializableValue::Reference {
259 object_id: r.object_number,
260 generation: r.generation_number,
261 },
262 }
263 }
264}
265
266pub struct GraphDeserializer;
267
268impl GraphDeserializer {
269 pub fn deserialize(serialized: SerializableGraph) -> Result<PdfAstGraph, String> {
270 let mut ast = PdfAstGraph::new();
271 let mut id_map: HashMap<usize, NodeId> = HashMap::new();
272
273 for serialized_node in &serialized.nodes {
275 let node_type =
276 Self::parse_node_type(&serialized_node.node_type, serialized_node.object_id)?;
277 let value = Self::deserialize_value(&serialized_node.value)?;
278 let node_id = ast.create_node(node_type, value);
279
280 id_map.insert(serialized_node.id, node_id);
286 }
287
288 for serialized_edge in &serialized.edges {
290 let from_id = id_map
291 .get(&serialized_edge.from)
292 .ok_or_else(|| format!("Invalid from node ID: {}", serialized_edge.from))?;
293 let to_id = id_map
294 .get(&serialized_edge.to)
295 .ok_or_else(|| format!("Invalid to node ID: {}", serialized_edge.to))?;
296 let edge_type = Self::parse_edge_type(&serialized_edge.edge_type)?;
297
298 ast.add_edge(*from_id, *to_id, edge_type);
299 }
300
301 if let Some(root_serial_id) = serialized.root {
303 if let Some(&root_id) = id_map.get(&root_serial_id) {
304 ast.set_root(root_id);
305 }
306 }
307
308 Ok(ast)
309 }
310
311 fn parse_node_type(
312 type_str: &str,
313 object_id: Option<(u32, u16)>,
314 ) -> Result<crate::ast::NodeType, String> {
315 use crate::types::ObjectId;
316 match type_str {
317 "Root" => Ok(crate::ast::NodeType::Root),
318 "Catalog" => Ok(crate::ast::NodeType::Catalog),
319 "Pages" => Ok(crate::ast::NodeType::Pages),
320 "Page" => Ok(crate::ast::NodeType::Page),
321 "Resource" => Ok(crate::ast::NodeType::Resource),
322 "Font" => Ok(crate::ast::NodeType::Font),
323 "Image" => Ok(crate::ast::NodeType::Image),
324 "ContentStream" => Ok(crate::ast::NodeType::ContentStream),
325 "Annotation" => Ok(crate::ast::NodeType::Annotation),
326 "Action" => Ok(crate::ast::NodeType::Action),
327 "Metadata" => Ok(crate::ast::NodeType::Metadata),
328 "EmbeddedFile" => Ok(crate::ast::NodeType::EmbeddedFile),
329 "Signature" => Ok(crate::ast::NodeType::Signature),
330 "Object" => Ok(crate::ast::NodeType::Object(
331 object_id
332 .map(|(num, gen)| ObjectId::new(num, gen))
333 .unwrap_or_else(|| ObjectId::new(0, 0)),
334 )),
335 "Unknown" => Ok(crate::ast::NodeType::Unknown),
336 "Stream" => Ok(crate::ast::NodeType::Stream),
337 "FilteredStream" => Ok(crate::ast::NodeType::FilteredStream),
338 "DecodedStream" => Ok(crate::ast::NodeType::DecodedStream),
339 "XObject" => Ok(crate::ast::NodeType::XObject),
340 "FormXObject" => Ok(crate::ast::NodeType::FormXObject),
341 "ImageXObject" => Ok(crate::ast::NodeType::ImageXObject),
342 "Type1Font" => Ok(crate::ast::NodeType::Type1Font),
343 "TrueTypeFont" => Ok(crate::ast::NodeType::TrueTypeFont),
344 "Type3Font" => Ok(crate::ast::NodeType::Type3Font),
345 "CIDFont" => Ok(crate::ast::NodeType::CIDFont),
346 "JavaScriptAction" => Ok(crate::ast::NodeType::JavaScriptAction),
347 "GoToAction" => Ok(crate::ast::NodeType::GoToAction),
348 "URIAction" => Ok(crate::ast::NodeType::URIAction),
349 "LaunchAction" => Ok(crate::ast::NodeType::LaunchAction),
350 "SubmitFormAction" => Ok(crate::ast::NodeType::SubmitFormAction),
351 "AcroForm" => Ok(crate::ast::NodeType::AcroForm),
352 "Field" => Ok(crate::ast::NodeType::Field),
353 "Encrypt" => Ok(crate::ast::NodeType::Encrypt),
354 "Permission" => Ok(crate::ast::NodeType::Permission),
355 "ContentOperator" => Ok(crate::ast::NodeType::ContentOperator),
356 "TextOperator" => Ok(crate::ast::NodeType::TextOperator),
357 "GraphicsOperator" => Ok(crate::ast::NodeType::GraphicsOperator),
358 "EmbeddedJS" => Ok(crate::ast::NodeType::EmbeddedJS),
359 "SuspiciousAction" => Ok(crate::ast::NodeType::SuspiciousAction),
360 "ExternalReference" => Ok(crate::ast::NodeType::ExternalReference),
361 "EncodedContent" => Ok(crate::ast::NodeType::EncodedContent),
362 "Outline" => Ok(crate::ast::NodeType::Outline),
363 "OutlineItem" => Ok(crate::ast::NodeType::OutlineItem),
364 "NameTree" => Ok(crate::ast::NodeType::NameTree),
365 "StructTreeRoot" => Ok(crate::ast::NodeType::StructTreeRoot),
366 "StructElem" => Ok(crate::ast::NodeType::StructElem),
367 "ColorSpace" => Ok(crate::ast::NodeType::ColorSpace),
368 "ICCBased" => Ok(crate::ast::NodeType::ICCBased),
369 "Separation" => Ok(crate::ast::NodeType::Separation),
370 "DeviceN" => Ok(crate::ast::NodeType::DeviceN),
371 "Indexed" => Ok(crate::ast::NodeType::Indexed),
372 "Pattern" => Ok(crate::ast::NodeType::Pattern),
373 "Shading" => Ok(crate::ast::NodeType::Shading),
374 "ExtGState" => Ok(crate::ast::NodeType::ExtGState),
375 "Function" => Ok(crate::ast::NodeType::Function),
376 "CMap" => Ok(crate::ast::NodeType::CMap),
377 "ToUnicode" => Ok(crate::ast::NodeType::ToUnicode),
378 "Encoding" => Ok(crate::ast::NodeType::Encoding),
379 "OCG" => Ok(crate::ast::NodeType::OCG),
380 "OCProperties" => Ok(crate::ast::NodeType::OCProperties),
381 "OCMD" => Ok(crate::ast::NodeType::OCMD),
382 "RichMedia" => Ok(crate::ast::NodeType::RichMedia),
383 "Rendition" => Ok(crate::ast::NodeType::Rendition),
384 "Screen" => Ok(crate::ast::NodeType::Screen),
385 "Sound" => Ok(crate::ast::NodeType::Sound),
386 "Movie" => Ok(crate::ast::NodeType::Movie),
387 "ThreeD" => Ok(crate::ast::NodeType::ThreeD),
388 "U3D" => Ok(crate::ast::NodeType::U3D),
389 "PRC" => Ok(crate::ast::NodeType::PRC),
390 "OutputIntent" => Ok(crate::ast::NodeType::OutputIntent),
391 "LinkAnnotation" => Ok(crate::ast::NodeType::LinkAnnotation),
392 "WidgetAnnotation" => Ok(crate::ast::NodeType::WidgetAnnotation),
393 "FileAttachmentAnnotation" => Ok(crate::ast::NodeType::FileAttachmentAnnotation),
394 "InlineImage" => Ok(crate::ast::NodeType::InlineImage),
395 "Form" => Ok(crate::ast::NodeType::Form),
396 "Structure" => Ok(crate::ast::NodeType::Structure),
397 "Multimedia" => Ok(crate::ast::NodeType::Multimedia),
398 "JavaScript" => Ok(crate::ast::NodeType::JavaScript),
399 "Encryption" => Ok(crate::ast::NodeType::Encryption),
400 "Content" => Ok(crate::ast::NodeType::Content),
401 "Other" => Ok(crate::ast::NodeType::Other),
402 _ => Ok(crate::ast::NodeType::Unknown),
403 }
404 }
405
406 fn parse_edge_type(type_str: &str) -> Result<EdgeType, String> {
407 match type_str {
408 "Child" => Ok(EdgeType::Child),
409 "Reference" => Ok(EdgeType::Reference),
410 "Parent" => Ok(EdgeType::Parent),
411 "Resource" => Ok(EdgeType::Resource),
412 "Annotation" => Ok(EdgeType::Annotation),
413 "Content" => Ok(EdgeType::Content),
414 _ => Err(format!("Unknown edge type: {}", type_str)),
415 }
416 }
417
418 fn deserialize_value(value: &SerializableValue) -> Result<PdfValue, String> {
419 match value {
420 SerializableValue::Null => Ok(PdfValue::Null),
421 SerializableValue::Boolean(b) => Ok(PdfValue::Boolean(*b)),
422 SerializableValue::Integer(i) => Ok(PdfValue::Integer(*i)),
423 SerializableValue::Real(r) => Ok(PdfValue::Real(*r)),
424 SerializableValue::String(s) => Ok(PdfValue::String(
425 crate::types::PdfString::new_literal(s.as_bytes()),
426 )),
427 SerializableValue::Name(n) => Ok(PdfValue::Name(crate::types::PdfName::new(n))),
428 SerializableValue::Array(items) => {
429 let mut array = crate::types::PdfArray::new();
430 for item in items {
431 array.push(Self::deserialize_value(item)?);
432 }
433 Ok(PdfValue::Array(array))
434 }
435 SerializableValue::Dictionary(map) => {
436 let mut dict = crate::types::PdfDictionary::new();
437 for (key, val) in map {
438 dict.insert(key.as_str(), Self::deserialize_value(val)?);
439 }
440 Ok(PdfValue::Dictionary(dict))
441 }
442 SerializableValue::Stream {
443 dictionary,
444 data,
445 lazy,
446 } => {
447 let mut dict = crate::types::PdfDictionary::new();
448 for (key, val) in dictionary {
449 dict.insert(key.as_str(), Self::deserialize_value(val)?);
450 }
451 let stream = if let Some(reference) = lazy {
452 crate::types::PdfStream::new_lazy(dict, reference.clone())
453 } else {
454 crate::types::PdfStream {
455 dict,
456 data: crate::types::StreamData::Raw(data.clone()),
457 }
458 };
459 Ok(PdfValue::Stream(stream))
460 }
461 SerializableValue::Reference {
462 object_id,
463 generation,
464 } => Ok(PdfValue::Reference(crate::types::PdfReference {
465 object_number: *object_id,
466 generation_number: *generation,
467 })),
468 }
469 }
470}
471
472pub fn to_json(document: &PdfDocument) -> Result<String, serde_json::Error> {
474 let serializable = SerializableDocument::from_document(document);
475 serde_json::to_string_pretty(&serializable)
476}
477
478impl SerializableDocument {
479 pub fn from_document(document: &PdfDocument) -> Self {
480 let ast_serializable = SerializableGraph::from_ast(&document.ast);
481
482 let mut xref_entries = HashMap::new();
484 for (obj_id, entry) in &document.xref.entries {
485 let key = format!("{}_{}", obj_id.number, obj_id.generation);
486 let serializable_entry = match entry {
487 crate::ast::document::XRefEntry::InUse { offset, generation } => {
488 SerializableXRefEntry {
489 offset: Some(*offset),
490 generation: *generation,
491 entry_type: "InUse".to_string(),
492 }
493 }
494 crate::ast::document::XRefEntry::Free { generation, .. } => SerializableXRefEntry {
495 offset: None,
496 generation: *generation,
497 entry_type: "Free".to_string(),
498 },
499 crate::ast::document::XRefEntry::Compressed { .. } => SerializableXRefEntry {
500 offset: None,
501 generation: 0,
502 entry_type: "Compressed".to_string(),
503 },
504 };
505 xref_entries.insert(key, serializable_entry);
506 }
507
508 let catalog_serial_id = document.catalog.and_then(|node_id| {
510 ast_serializable
511 .nodes
512 .iter()
513 .find(|node| node.id == node_id.0)
514 .map(|node| node.id)
515 });
516
517 let info_serial_id = document.info.and_then(|node_id| {
518 ast_serializable
519 .nodes
520 .iter()
521 .find(|node| node.id == node_id.0)
522 .map(|node| node.id)
523 });
524
525 SerializableDocument {
526 version: document.version.to_string(),
527 schema_version: AST_SCHEMA_VERSION.to_string(),
528 ast: ast_serializable,
529 catalog: catalog_serial_id,
530 info: info_serial_id,
531 trailer: GraphSerializer::serialize_value(&PdfValue::Dictionary(
532 document.trailer.clone(),
533 )),
534 xref_entries,
535 metadata: SerializableDocumentMetadata {
536 file_size: document.metadata.file_size,
537 linearized: document.metadata.linearized,
538 encrypted: document.metadata.encrypted,
539 has_forms: document.metadata.has_forms,
540 has_xfa: document.metadata.has_xfa,
541 xfa_packets: document.metadata.xfa_packets,
542 has_xfa_scripts: document.metadata.has_xfa_scripts,
543 xfa_script_nodes: document.metadata.xfa_script_nodes,
544 has_hybrid_forms: document.metadata.has_hybrid_forms,
545 form_field_count: document.metadata.form_field_count,
546 has_javascript: document.metadata.has_javascript,
547 has_embedded_files: document.metadata.has_embedded_files,
548 has_signatures: document.metadata.has_signatures,
549 has_richmedia: document.metadata.has_richmedia,
550 richmedia_annotations: document.metadata.richmedia_annotations,
551 richmedia_assets: document.metadata.richmedia_assets,
552 richmedia_scripts: document.metadata.richmedia_scripts,
553 has_3d: document.metadata.has_3d,
554 threed_annotations: document.metadata.threed_annotations,
555 threed_u3d: document.metadata.threed_u3d,
556 threed_prc: document.metadata.threed_prc,
557 has_audio: document.metadata.has_audio,
558 audio_annotations: document.metadata.audio_annotations,
559 has_video: document.metadata.has_video,
560 video_annotations: document.metadata.video_annotations,
561 has_dss: document.metadata.has_dss,
562 dss_vri_count: document.metadata.dss_vri_count,
563 dss_certs: document.metadata.dss_certs,
564 dss_ocsp: document.metadata.dss_ocsp,
565 dss_crl: document.metadata.dss_crl,
566 dss_timestamps: document.metadata.dss_timestamps,
567 page_count: document.metadata.page_count,
568 producer: document.metadata.producer.clone(),
569 creator: document.metadata.creator.clone(),
570 creation_date: document.metadata.creation_date.clone(),
571 modification_date: document.metadata.modification_date.clone(),
572 },
573 }
574 }
575}
576
577#[cfg(test)]
578mod tests {
579 use super::*;
580 use crate::ast::{NodeType, PdfAstGraph, PdfDocument, PdfVersion};
581 use crate::types::{PdfDictionary, PdfValue};
582
583 #[test]
584 fn test_graph_serialization() {
585 let mut ast = PdfAstGraph::new();
586 let root_value = PdfValue::Dictionary(PdfDictionary::new());
587 let root_id = ast.create_node(NodeType::Root, root_value);
588 ast.set_root(root_id);
589
590 let serialized = SerializableGraph::from_ast(&ast);
591 assert_eq!(serialized.nodes.len(), 1);
592 assert_eq!(serialized.edges.len(), 0);
593 assert!(serialized.root.is_some());
594
595 let json = serialized.to_json().unwrap();
596 assert!(json.contains("Root"));
597
598 let deserialized = SerializableGraph::from_json(&json).unwrap();
599 assert_eq!(deserialized.nodes.len(), 1);
600 }
601
602 #[test]
603 fn test_document_serialization() {
604 let version = PdfVersion::new(1, 7);
605 let document = PdfDocument::new(version);
606
607 let json = to_json(&document).unwrap();
608 assert!(json.contains("1.7"));
609 assert!(json.contains("ast"));
610 assert!(json.contains("metadata"));
611 assert!(json.contains("schema_version"));
612 }
613
614 #[test]
615 fn test_cbor_serialization() {
616 let mut ast = PdfAstGraph::new();
617 let root_value = PdfValue::Dictionary(PdfDictionary::new());
618 let root_id = ast.create_node(NodeType::Root, root_value);
619 ast.set_root(root_id);
620
621 let serialized = SerializableGraph::from_ast(&ast);
622 let cbor_data = serialized.to_cbor().unwrap();
623 assert!(!cbor_data.is_empty());
624
625 let deserialized = SerializableGraph::from_cbor(&cbor_data).unwrap();
626 assert_eq!(deserialized.nodes.len(), 1);
627 }
628}