vrp_cli/extensions/solve/
formats.rs1use 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#[allow(clippy::type_complexity)]
15pub struct ProblemReader(pub Box<dyn Fn(File, Option<Vec<File>>) -> Result<Problem, GenericError>>);
16
17pub struct InitSolutionReader(pub Box<dyn Fn(File, Arc<Problem>) -> Result<Solution, GenericError>>);
19
20#[allow(clippy::type_complexity)]
21pub 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#[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
40pub 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}