snark_tool/procedure/basic_procedures/
write.rs1use crate::graph::graph::GraphConstructor;
2use crate::graph::undirected::UndirectedGraph;
3use crate::procedure::basic_procedures::read;
4use crate::procedure::error::Error;
5use crate::procedure::helpers::config_helper;
6use crate::procedure::procedure::{GraphProperties, Procedure, Result};
7use crate::procedure::procedure_builder::{ConfigMap, ProcedureBuilder};
8use crate::service::io::error::{ReadError, WriteError};
9use crate::service::io::writer_ba::BaWriter;
10use crate::service::io::writer_g6::G6Writer;
11use crate::service::io::writer_s6::S6Writer;
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14use std::fs::OpenOptions;
15use std::io::Write;
16use std::{fs, marker, path};
17
18const FILE_NAME: &str = "file";
20const GRAPH_FORMAT: &str = "graph-format";
21const WITH_PROPERTIES: &str = "with-properties";
22
23const DEFAULT_FILE_NAME: &str = "write-procedure-output-file";
24
25struct WriteProcedure<G: UndirectedGraph> {
26 config: WriteProcedureConfig,
27 _ph: marker::PhantomData<G>,
28}
29
30pub struct WriteProcedureBuilder {}
31
32pub struct WriteProcedureConfig {
33 file_path: String,
34 graph_format: String,
35 with_properties: bool,
36}
37
38impl<G: UndirectedGraph> Procedure<G> for WriteProcedure<G> {
39 fn run(&self, graphs: &mut Vec<(G, GraphProperties)>) -> Result<()> {
40 println!("running write procedure");
41 self.write_graphs(graphs)
42 }
43}
44
45impl<G: UndirectedGraph> WriteProcedure<G> {
46 pub fn write_graphs(&self, graphs: &mut Vec<(G, GraphProperties)>) -> Result<()>
47 where
48 G: UndirectedGraph,
49 {
50 let file_path = self.config.file_path();
51 let graph_format = self.config.graph_format();
52 let with_properties = self.config.with_properties();
53
54 if with_properties {
55 return self.write_with_properties(graphs, graph_format, file_path);
56 }
57 self.write_without_properties(graphs, graph_format, file_path)
58 }
59
60 fn write_without_properties(
61 &self,
62 graphs: &mut Vec<(G, GraphProperties)>,
63 graph_format: &String,
64 file_path: &String,
65 ) -> Result<()> {
66 match graph_format.as_str() {
67 read::G6_FORMAT => {
68 G6Writer::write_graphs_to_file(&graphs, file_path)?;
69 }
70 read::BA_FORMAT => {
71 BaWriter::write_graphs_to_file(graphs, file_path)?;
72 }
73 read::S6_FORMAT => {
74 S6Writer::write_graphs_to_file(graphs, file_path)?;
75 }
76 _ => {
77 return Err(Error::ConfigError(String::from(
78 "unknown graph format for read procedure",
79 )));
80 }
81 }
82 Ok(())
83 }
84
85 fn write_with_properties(
86 &self,
87 graphs: &mut Vec<(G, GraphProperties)>,
88 graph_format: &String,
89 file_path: &String,
90 ) -> Result<()> {
91 let mut file = Self::open_file_to_write(file_path)?;
92 let mut vec = vec![];
93 for graph in graphs {
94 let graph_string;
95 match graph_format.as_str() {
96 read::G6_FORMAT => {
97 graph_string = G6Writer::graph_to_g6_string(&graph.0);
98 }
99 read::S6_FORMAT => {
100 graph_string = S6Writer::graph_to_s6_string(&graph.0);
101 }
102 _ => {
103 return Err(Error::ConfigError(format!(
104 "unknown graph format: '{}' for procedure: {}",
105 graph_format,
106 WriteProcedureConfig::PROC_TYPE
107 )));
108 }
109 }
110 let graph_with_properties = GraphWithProperties {
111 graph: graph_string,
112 properties: graph.1.clone(),
113 graph_format: graph_format.clone(),
114 };
115 vec.push(graph_with_properties);
116 }
117 let serialized = serde_json::to_string_pretty(&vec).unwrap();
118 let result = writeln!(file, "{}", serialized);
119 if let Err(err) = result {
120 return Err(Error::WriteError(WriteError {
121 message: format!("error while writing to file: {}, error: {}", file_path, err),
122 }));
123 }
124 Ok(())
125 }
126
127 fn open_file_to_write<P: AsRef<path::Path>>(path: P) -> Result<fs::File> {
128 let file_result = OpenOptions::new().write(true).create(true).open(&path);
129 if file_result.is_err() {
130 return Err(Error::ReadError(ReadError {
131 message: format!("open file to write error for file: {:?}", path.as_ref()),
132 }));
133 }
134 Ok(file_result.unwrap())
135 }
136}
137
138#[derive(Serialize, Deserialize, Debug)]
139pub struct GraphWithProperties {
140 pub graph: String,
141 pub properties: GraphProperties,
142 pub graph_format: String,
143}
144
145impl WriteProcedureConfig {
146 pub const PROC_TYPE: &'static str = "write";
147
148 pub fn new(file_path: String, graph_format: String, with_properties: bool) -> Self {
149 WriteProcedureConfig {
150 file_path,
151 graph_format,
152 with_properties,
153 }
154 }
155
156 pub fn default() -> Self {
157 WriteProcedureConfig {
158 file_path: DEFAULT_FILE_NAME.to_string(),
159 graph_format: read::G6_FORMAT.to_string(),
160 with_properties: false,
161 }
162 }
163
164 pub fn from_proc_config(config: &HashMap<String, serde_json::Value>) -> Result<Self> {
165 let file_path = config_helper::resolve_value_or_default(
166 &config,
167 FILE_NAME,
168 DEFAULT_FILE_NAME.to_string(),
169 Self::PROC_TYPE,
170 )?;
171 let graph_format = config_helper::resolve_value_or_default(
172 &config,
173 GRAPH_FORMAT,
174 read::G6_FORMAT.to_string(),
175 Self::PROC_TYPE,
176 )?;
177 let with_properties =
178 config_helper::resolve_value(&config, WITH_PROPERTIES, Self::PROC_TYPE)?;
179
180 let result = WriteProcedureConfig {
181 file_path,
182 graph_format,
183 with_properties,
184 };
185 Ok(result)
186 }
187 pub fn file_path(&self) -> &String {
188 &self.file_path
189 }
190
191 pub fn graph_format(&self) -> &String {
192 &self.graph_format
193 }
194
195 pub fn with_properties(&self) -> bool {
196 self.with_properties
197 }
198}
199
200impl<G: UndirectedGraph + GraphConstructor + 'static> ProcedureBuilder<G>
201 for WriteProcedureBuilder
202{
203 fn build_from_map(&self, config: ConfigMap) -> Result<Box<dyn Procedure<G>>> {
204 let proc_config = WriteProcedureConfig::from_proc_config(&config)?;
205 Ok(Box::new(WriteProcedure {
206 config: proc_config,
207 _ph: marker::PhantomData,
208 }))
209 }
210}
211
212impl WriteProcedureBuilder {
213 pub fn build<G: UndirectedGraph + 'static>(
214 config: WriteProcedureConfig,
215 ) -> Box<dyn Procedure<G>> {
216 Box::new(WriteProcedure {
217 config,
218 _ph: marker::PhantomData,
219 })
220 }
221}