snark_tool/procedure/basic_procedures/
read.rs

1use crate::graph::graph::{Graph, GraphConstructor};
2use crate::graph::undirected::UndirectedGraph;
3use crate::procedure::error::Error;
4use crate::procedure::helpers::config_helper;
5use crate::procedure::procedure::{GraphProperties, Procedure, Result};
6use crate::procedure::procedure_builder::{ConfigMap, ProcedureBuilder};
7use crate::service::io::error::ReadError;
8use crate::service::io::reader::GraphFileReader;
9use crate::service::io::reader_ba::BaReader;
10use crate::service::io::reader_g6::G6Reader;
11use crate::service::io::reader_json::JsonReader;
12use crate::service::io::reader_s6::S6Reader;
13use std::collections::HashMap;
14use std::{fs, marker, path};
15
16// config params
17const FILE_NAME: &str = "file";
18const GRAPH_FORMAT: &str = "graph-format";
19const NUMBER_OF_GRAPHS: &str = "number-of-graphs";
20
21// config param properties
22pub const G6_FORMAT: &str = "g6";
23pub const S6_FORMAT: &str = "s6";
24pub const BA_FORMAT: &str = "ba";
25pub const JSON_FORMAT: &str = "json";
26
27const DEFAULT_FILE_NAME: &str = "read-procedure-input-file.g6";
28
29struct ReadProcedure<G: Graph> {
30    config: ReadProcedureConfig,
31    _ph: marker::PhantomData<G>,
32}
33
34impl<G: UndirectedGraph + GraphConstructor> Procedure<G> for ReadProcedure<G> {
35    fn run(&self, graphs: &mut Vec<(G, GraphProperties)>) -> Result<()> {
36        println!("running read procedure");
37        self.read_graphs(graphs)
38    }
39}
40
41impl<G: UndirectedGraph + GraphConstructor> ReadProcedure<G> {
42    pub fn read_graphs(&self, graphs: &mut Vec<(G, GraphProperties)>) -> Result<()> {
43        let file_path = self.config.file_path();
44        let graphs_count = self.config.number_of_graphs();
45        let file = Self::open_file_to_read(file_path)?;
46        let graph_format = self.config.graph_format();
47
48        match graph_format.as_str() {
49            G6_FORMAT => {
50                let reader = G6Reader::new(&file);
51                Self::read_by_format(reader, graphs, graphs_count)?;
52            }
53            BA_FORMAT => {
54                let reader = BaReader::<G>::new(&file);
55                Self::read_by_format(reader, graphs, graphs_count)?;
56            }
57            S6_FORMAT => {
58                let reader = S6Reader::<G>::new(&file);
59                Self::read_by_format(reader, graphs, graphs_count)?;
60            }
61            JSON_FORMAT => {
62                Self::read_json_format(graphs, graphs_count, &file)?;
63            }
64            _ => {
65                return Err(Error::ConfigError(String::from(
66                    "unknown graph format for read procedure",
67                )));
68            }
69        }
70        Ok(())
71    }
72
73    fn read_by_format<'a, R>(
74        mut reader: R,
75        graphs: &mut Vec<(G, GraphProperties)>,
76        graphs_count: Option<usize>,
77    ) -> Result<()>
78    where
79        R: GraphFileReader<'a, G>,
80    {
81        let mut counter = 1;
82        let mut graph_opt = reader.next();
83        while graph_opt.is_some() {
84            let graph = graph_opt.unwrap()?;
85            let mut properties = GraphProperties::new();
86            properties.insert("size".to_string(), serde_json::to_value(graph.size())?);
87            graphs.push((graph, properties));
88            counter += 1;
89
90            if graphs_count.is_some() && graphs_count.unwrap() < counter {
91                break;
92            }
93            graph_opt = reader.next();
94        }
95        if graphs_count.is_some() && graphs_count.unwrap() > counter {
96            println!(
97                "You asked for: {} graphs but given file contains only {}",
98                graphs_count.unwrap(),
99                counter
100            );
101        }
102        Ok(())
103    }
104
105    fn read_json_format(
106        graphs: &mut Vec<(G, GraphProperties)>,
107        graphs_count: Option<usize>,
108        file: &fs::File,
109    ) -> Result<()> {
110        let mut counter = 1;
111
112        let mut reader = JsonReader::<G>::new(file);
113
114        let mut graph_opt = reader.next_with_properties();
115        while graph_opt.is_some() {
116            let graph = graph_opt.unwrap()?;
117            graphs.push(graph);
118            counter += 1;
119
120            if graphs_count.is_some() && graphs_count.unwrap() < counter {
121                break;
122            }
123
124            graph_opt = reader.next_with_properties();
125        }
126        if graphs_count.is_some() && graphs_count.unwrap() > counter {
127            println!(
128                "You asked for: {} graphs but given file contains only {}",
129                graphs_count.unwrap(),
130                counter
131            );
132        }
133        Ok(())
134    }
135
136    fn open_file_to_read<P: AsRef<path::Path>>(path: P) -> Result<fs::File> {
137        let file_result = fs::OpenOptions::new().read(true).open(&path);
138        if file_result.is_err() {
139            return Err(Error::ReadError(ReadError {
140                message: format!("open file to read error for file: {:?}", path.as_ref()),
141            }));
142        }
143        Ok(file_result.unwrap())
144    }
145}
146
147pub struct ReadProcedureConfig {
148    file_path: String,
149    graph_format: String,
150    number_of_graphs: Option<usize>,
151}
152
153impl ReadProcedureConfig {
154    pub const PROC_TYPE: &'static str = "read";
155
156    pub fn new(file_path: String, graph_format: String, number_of_graphs: Option<usize>) -> Self {
157        ReadProcedureConfig {
158            file_path,
159            graph_format,
160            number_of_graphs,
161        }
162    }
163
164    pub fn default() -> Self {
165        Self {
166            file_path: DEFAULT_FILE_NAME.to_string(),
167            graph_format: GRAPH_FORMAT.to_string(),
168            number_of_graphs: None,
169        }
170    }
171
172    pub fn from_proc_config(config: &HashMap<String, serde_json::Value>) -> Result<Self> {
173        let file_path = config_helper::resolve_value_or_default(
174            &config,
175            FILE_NAME,
176            DEFAULT_FILE_NAME.to_string(),
177            Self::PROC_TYPE,
178        )?;
179        let graph_format = config_helper::resolve_value_or_default(
180            &config,
181            GRAPH_FORMAT,
182            G6_FORMAT.to_string(),
183            Self::PROC_TYPE,
184        )?;
185        let number_of_graphs = config_helper::resolve_value_or_default(
186            &config,
187            NUMBER_OF_GRAPHS,
188            None,
189            Self::PROC_TYPE,
190        )?;
191        let result = ReadProcedureConfig {
192            file_path,
193            graph_format,
194            number_of_graphs,
195        };
196        Ok(result)
197    }
198
199    pub fn file_path(&self) -> &String {
200        &self.file_path
201    }
202
203    pub fn graph_format(&self) -> &String {
204        &self.graph_format
205    }
206
207    pub fn number_of_graphs(&self) -> Option<usize> {
208        self.number_of_graphs
209    }
210}
211
212pub struct ReadProcedureBuilder {}
213
214impl<G: UndirectedGraph + GraphConstructor + 'static> ProcedureBuilder<G> for ReadProcedureBuilder {
215    fn build_from_map(&self, config: ConfigMap) -> Result<Box<dyn Procedure<G>>> {
216        let proc_config = ReadProcedureConfig::from_proc_config(&config)?;
217        Ok(Box::new(ReadProcedure {
218            config: proc_config,
219            _ph: marker::PhantomData,
220        }))
221    }
222}
223
224impl ReadProcedureBuilder {
225    pub fn build<G: UndirectedGraph + GraphConstructor + 'static>(
226        config: ReadProcedureConfig,
227    ) -> Box<dyn Procedure<G>> {
228        Box::new(ReadProcedure {
229            config,
230            _ph: marker::PhantomData,
231        })
232    }
233}