Check for loops in relays
This commit is contained in:
		@@ -1,7 +1,7 @@
 | 
			
		||||
//! # Devices entities definition
 | 
			
		||||
 | 
			
		||||
use crate::constants::StaticConstraints;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::collections::{HashMap, HashSet};
 | 
			
		||||
 | 
			
		||||
/// Device information provided directly by the device during syncrhonisation.
 | 
			
		||||
///
 | 
			
		||||
@@ -165,7 +165,7 @@ impl DeviceRelay {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let relays_map = list.iter().map(|r| (r.id, r)).collect::<HashMap<_, _>>();
 | 
			
		||||
        let mut relays_map = list.iter().map(|r| (r.id, r)).collect::<HashMap<_, _>>();
 | 
			
		||||
 | 
			
		||||
        if self.depends_on.iter().any(|d| !relays_map.contains_key(d)) {
 | 
			
		||||
            return Some("A specified dependent relay does not exists!");
 | 
			
		||||
@@ -179,10 +179,73 @@ impl DeviceRelay {
 | 
			
		||||
            return Some("A specified conflicting relay does not exists!");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // TODO : check for loops
 | 
			
		||||
        // Check for loops in dependencies
 | 
			
		||||
        if self.check_for_loop_in_dependencies(&HashSet::new(), &relays_map) {
 | 
			
		||||
            return Some("A loop was detected in relay dependencies!");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check if relay is in conflicts with one of its dependencies
 | 
			
		||||
        let mut all_dependencies = HashSet::new();
 | 
			
		||||
        let mut all_conflicts = HashSet::new();
 | 
			
		||||
        self.get_list_of_dependencies_and_conflicts(
 | 
			
		||||
            &mut all_dependencies,
 | 
			
		||||
            &mut all_conflicts,
 | 
			
		||||
            &relays_map,
 | 
			
		||||
        );
 | 
			
		||||
        for conf_id in all_conflicts {
 | 
			
		||||
            if all_dependencies.contains(&conf_id) {
 | 
			
		||||
                return Some(
 | 
			
		||||
                    "The relay or one of its dependencies is in conflict with a dependency!",
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn check_for_loop_in_dependencies(
 | 
			
		||||
        &self,
 | 
			
		||||
        visited: &HashSet<DeviceRelayID>,
 | 
			
		||||
        list: &HashMap<DeviceRelayID, &Self>,
 | 
			
		||||
    ) -> bool {
 | 
			
		||||
        let mut clone = visited.clone();
 | 
			
		||||
        clone.insert(self.id);
 | 
			
		||||
 | 
			
		||||
        for d in &self.depends_on {
 | 
			
		||||
            if visited.contains(&d) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if list
 | 
			
		||||
                .get(&d)
 | 
			
		||||
                .expect("Missing a relay!")
 | 
			
		||||
                .check_for_loop_in_dependencies(&clone, list)
 | 
			
		||||
            {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_list_of_dependencies_and_conflicts(
 | 
			
		||||
        &self,
 | 
			
		||||
        deps_out: &mut HashSet<DeviceRelayID>,
 | 
			
		||||
        conflicts_out: &mut HashSet<DeviceRelayID>,
 | 
			
		||||
        list: &HashMap<DeviceRelayID, &Self>,
 | 
			
		||||
    ) {
 | 
			
		||||
        for d in &self.depends_on {
 | 
			
		||||
            let dependency = list.get(&d).expect("Missing a relay!");
 | 
			
		||||
 | 
			
		||||
            deps_out.insert(dependency.id);
 | 
			
		||||
 | 
			
		||||
            dependency.get_list_of_dependencies_and_conflicts(deps_out, conflicts_out, list);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for d in &self.conflicts_with {
 | 
			
		||||
            conflicts_out.insert(*d);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Device general information
 | 
			
		||||
@@ -213,7 +276,7 @@ impl DeviceGeneralInfo {
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::devices::device::DeviceRelay;
 | 
			
		||||
    use crate::devices::device::{DeviceRelay, DeviceRelayID};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn check_device_relay_error() {
 | 
			
		||||
@@ -238,5 +301,53 @@ mod tests {
 | 
			
		||||
        assert!(bad_name.error(&[]).is_some());
 | 
			
		||||
        assert_eq!(dep_on_unitary.error(&[unitary.clone()]), None);
 | 
			
		||||
        assert!(dep_on_unitary.error(&[]).is_some());
 | 
			
		||||
 | 
			
		||||
        // Dependency loop
 | 
			
		||||
        let mut dep_cycle_1 = DeviceRelay {
 | 
			
		||||
            id: DeviceRelayID::default(),
 | 
			
		||||
            name: "dep_cycle_1".to_string(),
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        };
 | 
			
		||||
        let dep_cycle_2 = DeviceRelay {
 | 
			
		||||
            id: DeviceRelayID::default(),
 | 
			
		||||
            name: "dep_cycle_2".to_string(),
 | 
			
		||||
            depends_on: vec![dep_cycle_1.id],
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        };
 | 
			
		||||
        let dep_cycle_3 = DeviceRelay {
 | 
			
		||||
            id: DeviceRelayID::default(),
 | 
			
		||||
            name: "dep_cycle_3".to_string(),
 | 
			
		||||
            depends_on: vec![dep_cycle_2.id],
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        };
 | 
			
		||||
        dep_cycle_1.depends_on = vec![dep_cycle_3.id];
 | 
			
		||||
 | 
			
		||||
        assert!(dep_cycle_1.error(&[dep_cycle_2, dep_cycle_3]).is_some());
 | 
			
		||||
 | 
			
		||||
        // Impossible conflict
 | 
			
		||||
        let other_dep = DeviceRelay {
 | 
			
		||||
            id: DeviceRelayID::default(),
 | 
			
		||||
            name: "other_dep".to_string(),
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        };
 | 
			
		||||
        let second_dep = DeviceRelay {
 | 
			
		||||
            id: DeviceRelayID::default(),
 | 
			
		||||
            name: "second_dep".to_string(),
 | 
			
		||||
            conflicts_with: vec![other_dep.id],
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        };
 | 
			
		||||
        let target_relay = DeviceRelay {
 | 
			
		||||
            id: DeviceRelayID::default(),
 | 
			
		||||
            name: "target_relay".to_string(),
 | 
			
		||||
            depends_on: vec![other_dep.id, second_dep.id],
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        assert!(target_relay
 | 
			
		||||
            .error(&[other_dep.clone(), second_dep.clone()])
 | 
			
		||||
            .is_some());
 | 
			
		||||
        assert!(target_relay
 | 
			
		||||
            .error(&[other_dep, second_dep, target_relay.clone()])
 | 
			
		||||
            .is_some());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user