vrp_cli/extensions/solve/
formats.rs

1//! Contains format readers and writers.
2
3use crate::get_locations_serialized;
4use std::collections::HashMap;
5use std::fs::File;
6use std::io::{BufReader, BufWriter, Write};
7use std::sync::Arc;
8use vrp_core::models::{Problem, Solution};
9use vrp_core::prelude::{GenericError, Random};
10use vrp_pragmatic::format::solution::{write_pragmatic, PragmaticOutputType};
11use vrp_scientific::tsplib::{TsplibProblem, TsplibSolution};
12
13/// A reader for problem.
14#[allow(clippy::type_complexity)]
15pub struct ProblemReader(pub Box<dyn Fn(File, Option<Vec<File>>) -> Result<Problem, GenericError>>);
16
17/// A reader for initial solution.
18pub struct InitSolutionReader(pub Box<dyn Fn(File, Arc<Problem>) -> Result<Solution, GenericError>>);
19
20#[allow(clippy::type_complexity)]
21/// A writer for solution.
22pub struct SolutionWriter(
23    pub  Box<
24        dyn Fn(
25            &Problem,
26            Solution,
27            BufWriter<Box<dyn Write>>,
28            Option<BufWriter<Box<dyn Write>>>,
29        ) -> Result<(), GenericError>,
30    >,
31);
32
33/// A writer for locations.
34#[allow(clippy::type_complexity)]
35pub struct LocationWriter(pub Box<dyn Fn(File, BufWriter<Box<dyn Write>>) -> Result<(), GenericError>>);
36
37#[allow(clippy::type_complexity)]
38type FormatMap<'a> = HashMap<&'a str, (ProblemReader, InitSolutionReader, SolutionWriter, LocationWriter)>;
39
40/// Gets available format readers/writers.
41pub fn get_formats<'a>(is_rounded: bool, random: Arc<dyn Random>) -> FormatMap<'a> {
42    let mut formats = FormatMap::default();
43
44    add_scientific(&mut formats, is_rounded, random.clone());
45    add_pragmatic(&mut formats, random);
46
47    formats
48}
49
50fn add_scientific(formats: &mut FormatMap, is_rounded: bool, random: Arc<dyn Random>) {
51    if cfg!(feature = "scientific-format") {
52        use vrp_scientific::common::read_init_solution;
53        use vrp_scientific::lilim::{LilimProblem, LilimSolution};
54        use vrp_scientific::solomon::{SolomonProblem, SolomonSolution};
55
56        formats.insert(
57            "solomon",
58            (
59                ProblemReader(Box::new(move |problem: File, matrices: Option<Vec<File>>| {
60                    assert!(matrices.is_none());
61                    BufReader::new(problem).read_solomon(is_rounded)
62                })),
63                InitSolutionReader(Box::new({
64                    let random = random.clone();
65                    move |file, problem| read_init_solution(BufReader::new(file), problem, random.clone())
66                })),
67                SolutionWriter(Box::new(|_, solution, mut writer, _| solution.write_solomon(&mut writer))),
68                LocationWriter(Box::new(|_, _| unimplemented!())),
69            ),
70        );
71        formats.insert(
72            "lilim",
73            (
74                ProblemReader(Box::new(move |problem: File, matrices: Option<Vec<File>>| {
75                    assert!(matrices.is_none());
76                    BufReader::new(problem).read_lilim(is_rounded)
77                })),
78                InitSolutionReader(Box::new(|_file, _problem| unimplemented!())),
79                SolutionWriter(Box::new(|_, solution, mut writer, _| solution.write_lilim(&mut writer))),
80                LocationWriter(Box::new(|_, _| unimplemented!())),
81            ),
82        );
83        formats.insert(
84            "tsplib",
85            (
86                ProblemReader(Box::new(move |problem: File, matrices: Option<Vec<File>>| {
87                    assert!(matrices.is_none());
88                    BufReader::new(problem).read_tsplib(is_rounded)
89                })),
90                InitSolutionReader(Box::new(move |file, problem| {
91                    read_init_solution(BufReader::new(file), problem, random.clone())
92                })),
93                SolutionWriter(Box::new(|_, solution, mut writer, _| solution.write_tsplib(&mut writer))),
94                LocationWriter(Box::new(|_, _| unimplemented!())),
95            ),
96        );
97    }
98}
99
100fn add_pragmatic(formats: &mut FormatMap, random: Arc<dyn Random>) {
101    use vrp_pragmatic::format::problem::{deserialize_problem, PragmaticProblem};
102    use vrp_pragmatic::format::solution::read_init_solution as read_init_pragmatic;
103
104    formats.insert(
105        "pragmatic",
106        (
107            ProblemReader(Box::new(|problem: File, matrices: Option<Vec<File>>| {
108                if let Some(matrices) = matrices {
109                    let matrices = matrices.into_iter().map(BufReader::new).collect();
110                    (BufReader::new(problem), matrices).read_pragmatic()
111                } else {
112                    BufReader::new(problem).read_pragmatic()
113                }
114                .map_err(From::from)
115            })),
116            InitSolutionReader(Box::new(move |file, problem| {
117                read_init_pragmatic(BufReader::new(file), problem, random.clone())
118            })),
119            SolutionWriter(Box::new(|problem, solution, mut default_writer, geojson_writer| {
120                geojson_writer
121                    .map_or(Ok(()), |mut geojson_writer| {
122                        write_pragmatic(problem, &solution, PragmaticOutputType::OnlyGeoJson, &mut geojson_writer)
123                    })
124                    .and_then(|_| write_pragmatic(problem, &solution, Default::default(), &mut default_writer))
125            })),
126            LocationWriter(Box::new(|problem, writer| {
127                let mut writer = writer;
128                deserialize_problem(BufReader::new(problem))
129                    .map_err(From::from)
130                    .and_then(|problem| get_locations_serialized(&problem))
131                    .and_then(|locations| writer.write_all(locations.as_bytes()).map_err(From::from))
132            })),
133        ),
134    );
135}