snark_tool/procedure/basic_procedures/
write.rs

1use 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
18// config params
19const 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}