fob_graph/memory/
serialization.rs1use super::super::external_dep::ExternalDependency;
4use super::super::{Module, ModuleId};
5use super::graph::{GraphInner, ModuleGraph};
6use crate::{Error, Result};
7use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
8use std::sync::Arc;
9
10fn escape_label(label: &str) -> String {
12 label.replace('"', "\\\"")
13}
14
15impl ModuleGraph {
16 pub fn to_dot_format(&self) -> Result<String> {
18 let mut output = String::from("digraph ModuleGraph {\n");
19 let all_modules = self.modules()?;
20
21 for module in &all_modules {
22 output.push_str(" \"");
23 output.push_str(&escape_label(&module.id.path_string()));
24 output.push_str("\";\n");
25 }
26
27 for module in &all_modules {
28 let deps = self.dependencies(&module.id)?;
29 for target in deps {
30 output.push_str(" \"");
31 output.push_str(&escape_label(&module.id.path_string()));
32 output.push_str("\" -> \"");
33 output.push_str(&escape_label(&target.path_string()));
34 output.push_str("\";\n");
35 }
36 }
37
38 output.push_str("}\n");
39 Ok(output)
40 }
41
42 pub fn to_json(&self) -> Result<String> {
44 let all_modules = self.modules()?;
45 let entry_points = self.entry_points()?;
46 let external_deps = self.external_dependencies()?;
47
48 #[derive(serde::Serialize)]
49 struct GraphJson {
50 modules: Vec<Module>,
51 entry_points: Vec<ModuleId>,
52 external_deps: Vec<ExternalDependency>,
53 }
54
55 let graph_json = GraphJson {
56 modules: all_modules,
57 entry_points,
58 external_deps,
59 };
60
61 serde_json::to_string_pretty(&graph_json)
62 .map_err(|e| Error::InvalidConfig(format!("Failed to serialize graph: {e}")))
63 }
64
65 pub fn to_bytes(&self) -> Result<Vec<u8>> {
79 const FORMAT_VERSION: u32 = 1;
80
81 let inner = self.inner.read();
82
83 #[derive(serde::Serialize)]
85 struct SerializedGraph {
86 version: u32,
87 modules: HashMap<ModuleId, Arc<Module>>,
88 dependencies: HashMap<ModuleId, HashSet<ModuleId>>,
89 dependents: HashMap<ModuleId, HashSet<ModuleId>>,
90 entry_points: HashSet<ModuleId>,
91 external_deps: HashMap<String, ExternalDependency>,
92 }
93
94 let serialized = SerializedGraph {
95 version: FORMAT_VERSION,
96 modules: inner.modules.clone(),
97 dependencies: inner.dependencies.clone(),
98 dependents: inner.dependents.clone(),
99 entry_points: inner.entry_points.clone(),
100 external_deps: inner.external_deps.clone(),
101 };
102
103 bincode::serde::encode_to_vec(&serialized, bincode::config::standard())
104 .map_err(|e| Error::InvalidConfig(format!("Failed to serialize graph to bytes: {e}")))
105 }
106
107 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
115 const FORMAT_VERSION: u32 = 1;
116
117 #[derive(serde::Deserialize)]
118 struct SerializedGraph {
119 version: u32,
120 modules: HashMap<ModuleId, Arc<Module>>,
121 dependencies: HashMap<ModuleId, HashSet<ModuleId>>,
122 dependents: HashMap<ModuleId, HashSet<ModuleId>>,
123 entry_points: HashSet<ModuleId>,
124 external_deps: HashMap<String, ExternalDependency>,
125 }
126
127 let (serialized, _): (SerializedGraph, _) =
128 bincode::serde::decode_from_slice(bytes, bincode::config::standard()).map_err(|e| {
129 Error::InvalidConfig(format!("Failed to deserialize graph from bytes: {e}"))
130 })?;
131
132 if serialized.version != FORMAT_VERSION {
134 return Err(Error::InvalidConfig(format!(
135 "Incompatible graph format version: expected {}, got {}",
136 FORMAT_VERSION, serialized.version
137 )));
138 }
139
140 let inner = GraphInner {
142 modules: serialized.modules,
143 dependencies: serialized.dependencies,
144 dependents: serialized.dependents,
145 entry_points: serialized.entry_points,
146 external_deps: serialized.external_deps,
147 };
148
149 Ok(ModuleGraph {
150 inner: Arc::new(parking_lot::RwLock::new(inner)),
151 })
152 }
153}