use crate::{Device, Error, PrintStatus, Room}; use std::collections::HashMap; #[derive(Debug)] pub struct House { rooms: HashMap, } impl House { pub fn new(rooms: HashMap) -> Self { Self { rooms } } pub fn get_room(&self, key: &str) -> Option<&Room> { self.rooms.get(key) } pub fn get_room_mut(&mut self, key: &str) -> Option<&mut Room> { self.rooms.get_mut(key) } pub fn insert_room(&mut self, name: &str, room: Room) -> Option { self.rooms.insert(name.to_string(), room) } pub fn remove_room(&mut self, key: &str) -> Option { self.rooms.remove(key) } pub fn get_device(&self, room_name: &str, device_name: &str) -> Result<&Device, Error> { let Some(room) = self.get_room(room_name) else { return Err(Error::new(format!("no room named '{}' found", room_name))); }; let Some(device) = room.get_device(device_name) else { return Err(Error::new(format!("no device named '{}' found in room '{}'", device_name, room_name))); }; Ok(device) } } impl PrintStatus for House { fn print_status(&self) { for (room_name, room) in self.rooms.iter() { println!("{}:", room_name); println!("{}", "-".repeat(32)); room.print_status(); println!(); } } } #[cfg(test)] mod tests { use super::*; use crate::{Device, PowerSocket, Thermometer}; fn create_test_house() -> House { House::new( [ ( "main".to_string(), Room::new( [ ("ThermA".to_string(), Thermometer::stub(20.0).into()), ("PSocA".to_string(), PowerSocket::stub(12.34, false).into()), ("PSocB".to_string(), PowerSocket::stub(10.01, true).into()), ] .into_iter() .collect(), ), ), ( "bedroom".to_string(), Room::new( [ ("PSocC".to_string(), PowerSocket::stub(11.11, true).into()), ("ThermB".to_string(), Thermometer::stub(17.99).into()), ] .into_iter() .collect(), ), ), ] .into_iter() .collect(), ) } #[test] fn smoke_test() { let mut house = create_test_house(); assert_eq!(house.rooms.len(), 2); house.print_status(); assert_eq!( format!("{}", house.get_room("main").unwrap().get_device("ThermA").unwrap().display()), "DEV:Thermometer[ 20.0 ]" ); assert_eq!( format!("{}", house.get_room("main").unwrap().get_device("PSocA").unwrap().display()), "DEV:PowerSocket[ OFF : 0.0 ]" ); assert_eq!( format!("{}", house.get_room("bedroom").unwrap().get_device("PSocC").unwrap().display()), "DEV:PowerSocket[ ON : 11.1 ]" ); let Device::PowerSocket(powers_socket) = house.get_room_mut("main").unwrap().get_device_mut("PSocA").unwrap() else { unreachable!() }; powers_socket.set_on(true); assert_eq!( format!("{}", house.get_room("main").unwrap().get_device("PSocA").unwrap().display()), "DEV:PowerSocket[ ON : 12.3 ]" ); } #[test] fn check_out_of_bounds() { let house = create_test_house(); assert!(house.get_room("absent").is_none()); } #[test] fn test_add_remove() { let mut house = create_test_house(); let room = Room::new(HashMap::new()); let result = house.insert_room("empty", room); assert!(result.is_none()); assert_eq!(house.rooms.len(), 3); let Some(result) = house.remove_room("bedroom") else { unreachable!() }; assert_eq!(result.get_device("ThermB").unwrap().display().to_string(), "DEV:Thermometer[ 18.0 ]"); } #[test] fn test_get_device() { let house = create_test_house(); let result = house.get_device("empty", "dummy"); assert_eq!(result.unwrap_err().to_string(), "no room named 'empty' found"); let result = house.get_device("main", "dummy"); assert_eq!(result.unwrap_err().to_string(), "no device named 'dummy' found in room 'main'"); let result = house.get_device("main", "ThermA"); assert_eq!(result.unwrap().display().to_string(), "DEV:Thermometer[ 20.0 ]"); } }