1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//! This module provides functionality to validate problem definition for logical correctness.

use crate::format::problem::*;
use crate::format::{CoordIndex, FormatError};

/// A validation context which keeps essential information.
pub struct ValidationContext<'a> {
    /// An original problem.
    pub problem: &'a Problem,
    /// Routing matrices.
    pub matrices: Option<&'a Vec<Matrix>>,

    coord_index: CoordIndex,
    job_index: HashMap<String, Job>,
}

mod common;
use self::common::*;

mod jobs;
use self::jobs::validate_jobs;

mod objectives;
use self::objectives::validate_objectives;

mod vehicles;
use self::vehicles::validate_vehicles;

mod relations;
use self::relations::validate_relations;

mod routing;
use self::routing::validate_routing;
use hashbrown::HashMap;

impl<'a> ValidationContext<'a> {
    /// Creates an instance of `ValidationContext`.
    pub fn new(problem: &'a Problem, matrices: Option<&'a Vec<Matrix>>) -> Self {
        Self {
            problem,
            matrices,
            coord_index: CoordIndex::new(problem),
            job_index: problem.plan.jobs.iter().map(|job| (job.id.clone(), job.clone())).collect(),
        }
    }

    /// Validates problem on set of rules.
    pub fn validate(&self) -> Result<(), Vec<FormatError>> {
        let errors = validate_jobs(&self)
            .err()
            .into_iter()
            .chain(validate_vehicles(&self).err().into_iter())
            .chain(validate_objectives(&self).err().into_iter())
            .chain(validate_routing(&self).err().into_iter())
            .chain(validate_relations(&self).err().into_iter())
            .flatten()
            .collect::<Vec<_>>();

        if errors.is_empty() {
            Ok(())
        } else {
            Err(errors)
        }
    }

    /// Gets list of jobs from the problem.
    fn jobs(&self) -> impl Iterator<Item = &Job> {
        self.problem.plan.jobs.iter()
    }

    /// Gets list of vehicles from the problem.
    fn vehicles(&self) -> impl Iterator<Item = &VehicleType> {
        self.problem.fleet.vehicles.iter()
    }

    /// Gets a flat list of job tasks from the job.
    fn tasks(&self, job: &'a Job) -> Vec<&'a JobTask> {
        job.pickups
            .as_ref()
            .iter()
            .flat_map(|tasks| tasks.iter())
            .chain(job.deliveries.as_ref().iter().flat_map(|tasks| tasks.iter()))
            .chain(job.replacements.as_ref().iter().flat_map(|tasks| tasks.iter()))
            .chain(job.services.as_ref().iter().flat_map(|tasks| tasks.iter()))
            .collect()
    }
}

fn is_reserved_job_id(job_id: &str) -> bool {
    job_id == "departure" || job_id == "arrival" || job_id == "break" || job_id == "reload" || job_id == "dispatch"
}