Решение на Dungeons and Compilers от Даниел Пенчев

Обратно към всички решения

Към профила на Даниел Пенчев

Резултати

  • 17 точки от тестове
  • 0 бонус точки
  • 17 точки общо
  • 13 успешни тест(а)
  • 2 неуспешни тест(а)

Код

use std::{collections::HashMap, ops::Add};
use std::collections::{HashSet, VecDeque};
use std::io::BufRead;
use std::slice::Iter;
use std::str::FromStr;
use std::vec::IntoIter;
/// Различните грешки, които ще очакваме да върнете като резултат от някои невалидни операции.
/// Повече детайли по-долу.
///
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
/// Четирите посоки, в които може една стая да има съседи. Може да добавите още trait
/// имплементации, за да си улесните живота.
///
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
fn opposite(direction: Direction) -> Direction{
match direction{
Direction::North=> Direction::South,
Direction::South => Direction::North,
Direction::East => Direction::West,
Direction::West => Direction::East,
}
}
}
impl FromStr for Direction{
type Err = Errors;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"North" => Ok(Direction::North),
"South" => Ok(Direction::South),
"West" => Ok(Direction::West),
"East" => Ok(Direction::East),
_ => Err(Errors::DirectionParseError(s.to_string()))
}
}
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
pub struct Room {
pub name: String,
pub links: HashMap<Direction,String>
// Каквито други полета ви трябват
}
impl Room{
fn new(room_name: &str) -> Self{
Room{
name: String::from(room_name),
links: HashMap::new()
}
}
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
rooms: HashMap<String,Room>
}
#[derive(PartialEq)]
pub enum Token {
SectionHeader(String),
SectionSeparator,
RecordComponent(String),
RecordStart,
RecordEnd,
LinkComponentSeparator,
Unknown,
}
impl Dungeon {
/// Конструиране на празен Dungeon, в който няма никакви стаи.
///
pub fn new() -> Self {
Dungeon{
rooms: HashMap::new()
}
}
/// Добавяне на стая към Dungeon с име `name`. Връща `Ok(())` при успех. Ако вече има стая с
/// такова име, очакваме да върнете `Errors::DuplicateRoom` с името.
///
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
if self.rooms.contains_key(name){
return Err(Errors::DuplicateRoom(name.to_string()))
}
let room = Room::new(name);
self.rooms.insert(name.to_string(),room);
Ok(())
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.get(room_name){
Some(room) => Ok(room),
None => Err(Errors::UnknownRoom(room_name.to_string()))
}
}
/// Добавяне на съсед на дадена стая. След извикването на функцията, очакваме стаята с име
/// `room_name` да има връзка в посока `direction` със стаята с име `other_room_name`.
///
/// Също така очакваме `other_room_name` да има връзка с `room_name` в *обратната* посока.
///
/// Успешен резултат е `Ok(())`. В случай, че която от двете стаи не същестува, очакваме грешка
/// `Errors::UnknownRoom` със съответното име на липсваща стая. Ако и двете липсват, спокойно
/// върнете тази, която проверявате първо.
///
pub fn set_link(
&mut self,
room_name: &str,
direction: Direction,
other_room_name: &str,
) -> Result<(), Errors> {
if !self.rooms.contains_key(room_name){
return Err(Errors::UnknownRoom(room_name.to_string()))
}
else if !self.rooms.contains_key(other_room_name){
return Err(Errors::UnknownRoom(other_room_name.to_string()))
}
let first_room = self.rooms.get_mut(room_name).unwrap();
first_room.links.insert(direction,other_room_name.to_string());
let second_room = self.rooms.get_mut(other_room_name).unwrap();
second_room.links.insert(Direction::opposite(direction), room_name.to_string());
return Ok(())
}
/// Четене на съседа на стаята с име `room_name` в посока `direction`. Тук има няколко
/// варианта на изход:
///
/// - Ако подадената стая не съществува, очакваме грешка `Errors::UnknownRoom`
/// - Ако подадената стая няма съсед в тази посока, Ok(None) е смисления резултат
/// - Иначе, чакаме reference към `Room` структурата на въпросния съсед, опакована в `Ok(Some(`.
///
pub fn get_next_room(&self, room_name: &str, direction: Direction) -> Result<Option<&Room>, Errors> {
let room = self.get_room(room_name)?;
let target_room_name = match room.links.get(&direction){
Some(room_name) => room_name,
None => return Ok(None)
};
Ok(self.rooms.get(target_room_name))
}
/// match_prefix("- ", "- Foo") //=> Some("Foo")
/// match_prefix("- ", "Bar") //=> None
///
fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<&'b str> {
input.strip_prefix(prefix)
}
/// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
/// файл, или, ако тестваме, може да е просто колекция от байтове.
///
/// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
///
/// Вижте по-долу за обяснение на грешките, които очакваме.
///
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut dungeon = Dungeon::new();
let mut tokens = Dungeon::tokenize(reader)?.into_iter();
if tokens.len() == 0 {
return Err(Errors::LineParseError {line_number: 0})
}
let mut line_number = 1;
Dungeon::fill_rooms(&mut dungeon, &mut tokens, &mut line_number)?;
Dungeon::fill_links(&mut dungeon, &mut tokens, &mut line_number)?;
Ok(dungeon)
}
fn fill_rooms(dungeon: &mut Dungeon, tokens: &mut IntoIter<Token>, line_number: &mut usize) -> Result<(), Errors>{
let rooms_header = tokens.next();
if rooms_header.is_none() || rooms_header.unwrap() != Token::SectionHeader("Rooms".to_string()){
return Err(Errors::LineParseError {line_number: *line_number})
}
*line_number += 1;
let mut record_flag = false;
let mut room_added = false;
loop {
match tokens.next() {
None => return Err(Errors::LineParseError { line_number: *line_number }),
Some(token) => match token {
Token::RecordStart => record_flag = true,
Token::RecordEnd => {
if !record_flag {
return Err(Errors::LineParseError { line_number: *line_number });
}
record_flag = false;
room_added = false;
*line_number += 1;
},
Token::RecordComponent(room_name) => {
if room_added {
return Err(Errors::LineParseError { line_number: *line_number });
}
dungeon.add_room(room_name.as_str());
room_added = true;
},
Token::SectionSeparator => {
*line_number += 1;
break
},
_ => return Err(Errors::LineParseError { line_number: *line_number })
}
}
}
Ok(())
}
fn fill_links(dungeon: &mut Dungeon, tokens: &mut IntoIter<Token>, line_number: &mut usize) -> Result<(), Errors>{
let links_header = tokens.next();
if links_header.is_none() || links_header.unwrap() != Token::SectionHeader("Links".to_string()){
return Err(Errors::LineParseError {line_number: *line_number})
}
*line_number += 1;
let mut link_components = Vec::<String>::new();
let mut separator_occurred = true;
let mut record_flag = false;
loop {
match tokens.next() {
None => break,
Some(token) => match token {
Token::RecordStart => record_flag = true,
Token::RecordEnd => {
if link_components.len() != 3 || separator_occurred {
return Err(Errors::LineParseError {line_number: *line_number});
}
let first_room = link_components.get(0).unwrap();
let direction = Direction::from_str(link_components.get(1).unwrap())?;
let second_room = link_components.get(2).unwrap();
dungeon.set_link(first_room, direction, second_room)?;
link_components.clear();
record_flag = false;
*line_number += 1;
},
Token::RecordComponent(room_name) => {
if !record_flag || !separator_occurred {
return Err(Errors::LineParseError {line_number: *line_number});
}
link_components.push(room_name);
separator_occurred = false;
},
Token::LinkComponentSeparator => {
if separator_occurred {
return Err(Errors::LineParseError {line_number: *line_number});
}
separator_occurred = true;
}
_ => return Err(Errors::LineParseError {line_number: *line_number})
}
}
}
Ok(())
}
fn tokenize<B: BufRead>(reader: B) -> Result<Vec<Token>, Errors> {
let mut tokens = Vec::<Token>::new();
for wrapped_line in reader.lines(){
if let Err(err) = wrapped_line {
return Err(Errors::IoError(err))
}
let line = wrapped_line.unwrap();
if let Some(header_value) = Dungeon::match_prefix("## ",&line){
tokens.push(Token::SectionHeader(header_value.to_string()));
continue;
} else if line.is_empty() {
tokens.push(Token::SectionSeparator);
continue;
}
let mut parts = line.split_whitespace();
match parts.next(){
Some("-") => tokens.push(Token::RecordStart),
Some(x) => tokens.push(Token::RecordComponent(x.to_string())),
None => tokens.push(Token::Unknown),
}
for part in parts{
match part{
"->" => tokens.push(Token::LinkComponentSeparator),
x => tokens.push(Token::RecordComponent(x.to_string()))
}
}
tokens.push(Token::RecordEnd);
}
return Ok(tokens)
}
/// Търси път от `start_room_name` до `end_room_name` и го връща във вектор, пакетиран във
/// `Ok(Some(` ако намери.
///
/// Ако няма път между тези две стаи, връща `Ok(None)`.
///
/// Ако четенето на стаи в един момент върне грешка, очакваме да върнете грешката нагоре.
///
pub fn find_path(
&self,
start_room_name: &str,
end_room_name: &str
) -> Result<Option<Vec<&Room>>, Errors> {
if !self.rooms.contains_key(&end_room_name.to_string()){
return Err(Errors::UnknownRoom(end_room_name.to_string()))
}
let mut parents = HashMap::<String,String>::new();
parents.insert(start_room_name.to_string(),start_room_name.to_string());
let mut nodes_to_visit = VecDeque::<&str>::new();
nodes_to_visit.push_back(start_room_name);
loop {
let mut curr_room = match (&mut nodes_to_visit).pop_front(){
Some(room_name) => self.get_room(room_name)?,
None => return Ok(None)
};
if curr_room.name == end_room_name {
break
}
for next_room in curr_room.links.values() {
if parents.contains_key(&next_room.to_string()){
continue
}
parents.insert(next_room.to_string(),curr_room.name.clone());
nodes_to_visit.push_back(next_room);
}
}
let mut result = Vec::<&Room>::new();
let mut curr_room_name = end_room_name;
loop{
result.push(self.rooms.get(curr_room_name).unwrap());
if curr_room_name == start_room_name {
break
}
curr_room_name = parents.get(curr_room_name).unwrap();
}
result.reverse();
Ok(Some(result))
}
}
#[cfg(test)]
mod tests {
use std::ptr;
use crate::{Direction, Dungeon};
use crate::Room;
use crate::Errors;
#[test]
fn add_room_to_dungeon() {
let mut dungeon = Dungeon::new();
assert!(dungeon.add_room("test-room").is_ok());
assert!(dungeon.rooms.contains_key("test-room"));
assert_eq!(dungeon.rooms.get("test-room").unwrap().name, "test-room");
}
#[test]
fn add_already_existing_room_to_dungeon() {
let mut dungeon = Dungeon::new();
dungeon.rooms.insert(String::from("test-room"), Room::new("test-room"));
let result = dungeon.add_room("test-room");
assert!(result.is_err());
let expected_error: Result<(), Errors> = Err(Errors::DuplicateRoom(String::from("test-room")));
assert!(matches!(result, Err(Errors::DuplicateRoom(err_msg)) if err_msg == "test-room"));
}
#[test]
fn get_room_from_dungeon() {
let mut dungeon = Dungeon::new();
dungeon.rooms.insert(String::from("test-room"), Room::new("test-room"));
let result = dungeon.get_room("test-room");
assert!(result.is_ok());
assert_eq!(result.unwrap().name, "test-room")
}
#[test]
fn get_non_existing_room_from_dungeon() {
let dungeon = Dungeon::new();
let result = dungeon.get_room("non-existing-room");
assert!(result.is_err());
let expected_error: Result<(), Errors> = Err(Errors::UnknownRoom(String::from("non-existing-room")));
assert!(matches!(result, Err(Errors::UnknownRoom(err_msg)) if err_msg == "non-existing-room"));
}
#[test]
fn set_link_missing_first_room() {
let mut dungeon = Dungeon::new();
dungeon.rooms.insert("existing-room".to_string(),Room::new("existing-room"));
let result = dungeon.set_link("non-existing-room",Direction::East,"existing-room");
assert!(result.is_err());
assert!(matches!(result, Err(Errors::UnknownRoom(err_msg)) if err_msg == "non-existing-room"));
}
#[test]
fn set_link_missing_second_room() {
let mut dungeon = Dungeon::new();
dungeon.rooms.insert("existing-room".to_string(),Room::new("existing-room"));
let result = dungeon.set_link("existing-room",Direction::East,"non-existing-room");
assert!(result.is_err());
assert!(matches!(result, Err(Errors::UnknownRoom(err_msg)) if err_msg == "non-existing-room"));
}
#[test]
fn set_link() {
let mut dungeon = Dungeon::new();
dungeon.rooms.insert("existing-room-1".to_string(),Room::new("existing-room-1"));
dungeon.rooms.insert("existing-room-2".to_string(), Room::new("existing-room-2"));
create_link(&mut dungeon,"existing-room-1",Direction::North,"existing-room-1");
create_link(&mut dungeon,"existing-room-1",Direction::South,"existing-room-1");
let result = dungeon.set_link("existing-room-1",Direction::East,"existing-room-2");
assert!(result.is_ok());
verify_link(&dungeon, "existing-room-1", Direction::East, "existing-room-2");
verify_link(&dungeon, "existing-room-1", Direction::North, "existing-room-1");
verify_link(&dungeon, "existing-room-1", Direction::South, "existing-room-1");
verify_link(&dungeon, "existing-room-2", Direction::West, "existing-room-1");
}
#[test]
fn set_link_cycle() {
let mut dungeon = Dungeon::new();
dungeon.rooms.insert("existing-room".to_string(),Room::new("existing-room"));
let result = dungeon.set_link("existing-room",Direction::East,"existing-room");
assert!(result.is_ok());
verify_link(&dungeon, "existing-room", Direction::East, "existing-room");
verify_link(&dungeon, "existing-room", Direction::West, "existing-room");
}
#[test]
fn set_link_with_overwriting_source_room_links() {
let mut dungeon = Dungeon::new();
dungeon.rooms.insert("existing-room-1".to_string(),Room::new("existing-room-1"));
dungeon.rooms.insert("existing-room-2".to_string(), Room::new("existing-room-2"));
create_link(&mut dungeon,"existing-room-1",Direction::East,"existing-room-1");
create_link(&mut dungeon,"existing-room-1",Direction::West,"existing-room-1");
let result = dungeon.set_link("existing-room-1",Direction::East,"existing-room-2");
assert!(result.is_ok());
verify_link(&dungeon, "existing-room-1", Direction::West, "existing-room-1");
verify_link(&dungeon, "existing-room-1", Direction::East, "existing-room-2");
verify_link(&dungeon, "existing-room-2", Direction::West, "existing-room-1");
}
#[test]
fn set_link_with_overwriting_target_room_links() {
let mut dungeon = Dungeon::new();
dungeon.rooms.insert("existing-room-1".to_string(),Room::new("existing-room-1"));
dungeon.rooms.insert("existing-room-2".to_string(), Room::new("existing-room-2"));
create_link(&mut dungeon,"existing-room-2",Direction::East,"existing-room-2");
create_link(&mut dungeon,"existing-room-2",Direction::West,"existing-room-2");
let result = dungeon.set_link("existing-room-1",Direction::East,"existing-room-2");
assert!(result.is_ok());
verify_link(&dungeon, "existing-room-1", Direction::East, "existing-room-2");
verify_link(&dungeon, "existing-room-2", Direction::East, "existing-room-2");
verify_link(&dungeon, "existing-room-2", Direction::West, "existing-room-1");
}
#[test]
fn get_next_room_no_neighbour(){
let mut dungeon = Dungeon::new();
dungeon.rooms.insert("existing-room-1".to_string(),Room::new("existing-room-1"));
let result = dungeon.get_next_room("existing-room-1",Direction::North);
assert!(matches!(result,Ok(None)))
}
#[test]
fn get_next_room_missing_source_room(){
let mut dungeon = Dungeon::new();
let result = dungeon.get_next_room("non-existing-room",Direction::North);
let expected_error: Result<(), Errors> = Err(Errors::UnknownRoom(String::from("non-existing-room")));
assert!(matches!(result,Err(Errors::UnknownRoom(err_msg)) if err_msg == "non-existing-room"));
}
#[test]
fn get_next_room(){
let mut dungeon = Dungeon::new();
dungeon.rooms.insert("existing-room-1".to_string(),Room::new("existing-room-1"));
dungeon.rooms.insert("existing-room-2".to_string(),Room::new("existing-room-2"));
create_link(&mut dungeon,"existing-room-1",Direction::West,"existing-room-2");
let result = dungeon.get_next_room("existing-room-1",Direction::West);
assert!(result.is_ok());
let option = result.unwrap();
assert!(option.is_some());
let actual_room_ref = option.unwrap();
let expected_room_ref = dungeon.rooms.get("existing-room-2").unwrap();
assert!(ptr::eq(actual_room_ref, expected_room_ref));
}
fn verify_link(dungeon: &Dungeon, source_room_name: &str, direction: Direction, target_room_name: &str){
let source_room = dungeon.rooms.get(source_room_name).unwrap();
let link = source_room.links.get(&direction);
assert!(link.is_some());
assert!(matches!(link, Some(target_room_name)));
}
fn create_link(dungeon: &mut Dungeon, source_room_name: &str,direction: Direction, target_room_name: &str){
let source_room = dungeon.rooms.get_mut(source_room_name).unwrap();
source_room.links.insert(direction,target_room_name.to_string());
}
#[test]
fn from_reader_missing_rooms_header() {
let input = "- Entrance
- Hallway
## Links
- Entrance -> East -> Hallway";
// .trim() за да премахнем първия и последния ред:
let result = Dungeon::from_reader(input.as_bytes());
assert!(matches!(result, Err(Errors::LineParseError {line_number}) if line_number == 1));
}
#[test]
fn from_reader_missing_links_header() {
let input = "## Rooms
- Entrance
- Hallway
- Entrance -> East -> Hallway";
let result = Dungeon::from_reader(input.as_bytes());
assert!(matches!(result, Err(Errors::LineParseError {line_number}) if line_number == 5));
}
#[test]
fn from_reader_unknown_header() {
let input = "## Rooms
- Entrance
- Hallway
## Links
## Unknown
- Entrance -> East -> Hallway";
let result = Dungeon::from_reader(input.as_bytes());
assert!(matches!(result, Err(Errors::LineParseError {line_number}) if line_number == 6));
}
#[test]
fn from_reader_links_between_non_existing_rooms() {
let input = "## Rooms
- Entrance
- Hallway
## Links
- Hello -> East -> Entrance";
let result = Dungeon::from_reader(input.as_bytes());
assert!(matches!(result, Err(Errors::UnknownRoom(err_msg)) if err_msg == "Hello"));
}
#[test]
fn from_reader_no_rooms_and_no_links() {
let input = "## Rooms
## Links";
let dungeon = Dungeon::from_reader(input.as_bytes()).unwrap();
assert!(dungeon.rooms.is_empty());
}
#[test]
fn from_reader() {
let input = "## Rooms
- 👾Entrance👾
- 👾Hallway👾
## Links
- 👾Entrance👾 -> East -> 👾Hallway👾";
let dungeon = Dungeon::from_reader(input.as_bytes()).unwrap();
assert_eq!(dungeon.get_room("👾Entrance👾").unwrap().name, "👾Entrance👾");
assert_eq!(dungeon.get_room("👾Hallway👾").unwrap().name, "👾Hallway👾");
assert_eq!(dungeon.get_next_room("👾Entrance👾", Direction::East).unwrap().unwrap().name, "👾Hallway👾");
}
#[test]
fn from_reader_invaid_direction() {
let input = "## Rooms
- Entrance
- Hallway
## Links
- Hello -> Invalid -> Entrance";
let result = Dungeon::from_reader(input.as_bytes());
assert!(matches!(result, Err(Errors::DirectionParseError(err_msg)) if err_msg == "Invalid"));
}
#[test]
fn from_reader_invalid_separator() {
let input = "## Rooms
- Entrance
- Hallway
>>>>>>>>>>>>>>>>>>>>>
## Links
- Hello -> Invalid -> Entrance";
let result = Dungeon::from_reader(input.as_bytes());
assert!(matches!(result, Err(Errors::LineParseError{line_number}) if line_number == 4));
}
#[test]
fn from_reader_invalid_separator_in_link() {
let input = "## Rooms
- Entrance
- Hallway
## Links
- Hello <-> Invalid -> Entrance";
let result = Dungeon::from_reader(input.as_bytes());
assert!(matches!(result, Err(Errors::LineParseError{line_number}) if line_number == 6));
}
#[test]
fn from_reader_invalid_room_record() {
let input = "## Rooms
Entrance
- Hallway
## Links
- Hello -> Invalid -> Entrance";
let result = Dungeon::from_reader(input.as_bytes());
assert!(matches!(result, Err(Errors::LineParseError{line_number}) if line_number == 2));
}
#[test]
fn from_reader_invalid_link_record() {
let input = "## Rooms
- Entrance
- Hallway
## Links
Hello -> Invalid -> Entrance";
let result = Dungeon::from_reader(input.as_bytes());
assert!(matches!(result, Err(Errors::LineParseError{line_number}) if line_number == 6));
}
#[test]
fn from_reader_empty_input() {
let input = "";
let result = Dungeon::from_reader(input.as_bytes());
assert!(matches!(result, Err(Errors::LineParseError{line_number}) if line_number == 0));
}
#[test]
fn find_path() {
let mut dungeon = Dungeon::new();
dungeon.add_room("A").unwrap();
dungeon.add_room("B").unwrap();
dungeon.add_room("C").unwrap();
dungeon.set_link("C", Direction::West,"A").unwrap();
dungeon.set_link("C", Direction::North,"B").unwrap();
dungeon.set_link("B",Direction::South, "A").unwrap();
let path = dungeon.find_path("A", "A").unwrap().unwrap();
assert_eq!(path.len(),1);
assert_eq!(path.get(0).unwrap().name,"A");
}
#[test]
fn find_path_no_path() {
let mut dungeon = Dungeon::new();
dungeon.add_room("A").unwrap();
dungeon.add_room("B").unwrap();
dungeon.add_room("C").unwrap();
dungeon.add_room("D").unwrap();
dungeon.set_link("A", Direction::West, "B").unwrap();
dungeon.set_link("A", Direction::East,"C").unwrap();
dungeon.set_link("C", Direction::West,"A").unwrap();
dungeon.set_link("C", Direction::North,"B").unwrap();
dungeon.set_link("B",Direction::South, "A").unwrap();
let path = dungeon.find_path("A", "D");
assert!(matches!(path, Ok(None)));
}
#[test]
fn find_path_multiple_rooms() {
let mut dungeon = Dungeon::new();
dungeon.add_room("A").unwrap();
dungeon.add_room("B").unwrap();
dungeon.add_room("C").unwrap();
dungeon.add_room("D").unwrap();
dungeon.add_room("E").unwrap();
dungeon.add_room("F").unwrap();
dungeon.set_link("A", Direction::West, "B").unwrap();
dungeon.set_link("A", Direction::East,"C").unwrap();
dungeon.set_link("C", Direction::West,"A").unwrap();
dungeon.set_link("C", Direction::North,"B").unwrap();
dungeon.set_link("B", Direction::South,"C").unwrap();
dungeon.set_link("C",Direction::East,"E").unwrap();
dungeon.set_link("B",Direction::North,"D").unwrap();
dungeon.set_link("D",Direction::West,"E").unwrap();
let path = dungeon.find_path("A", "E").unwrap().unwrap();
assert_eq!(path.len(), 3);
assert_eq!(path.get(0).unwrap().name, "A");
assert_eq!(path.get(1).unwrap().name, "C");
assert_eq!(path.get(2).unwrap().name, "E");
}
#[test]
fn find_path_invalid_start_room() {
let mut dungeon = Dungeon::new();
dungeon.add_room("A").unwrap();
dungeon.add_room("B").unwrap();
dungeon.set_link("A", Direction::West, "B").unwrap();
let path = dungeon.find_path("E", "A");
assert!(matches!(path, Err(Errors::UnknownRoom(err_msg)) if err_msg == "E"));
}
#[test]
fn find_path_invalid_target_room() {
let mut dungeon = Dungeon::new();
dungeon.add_room("A").unwrap();
dungeon.add_room("B").unwrap();
dungeon.set_link("A", Direction::West, "B").unwrap();
let path = dungeon.find_path("A", "E");
assert!(matches!(path, Err(Errors::UnknownRoom(err_msg)) if err_msg == "E"));
}
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20220116-3533338-qtdb0k/solution)
warning: unused import: `ops::Add`
 --> src/lib.rs:1:33
  |
1 | use std::{collections::HashMap, ops::Add};
  |                                 ^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: unused import: `HashSet`
 --> src/lib.rs:2:28
  |
2 |     use std::collections::{HashSet, VecDeque};
  |                            ^^^^^^^

warning: unused import: `std::slice::Iter`
 --> src/lib.rs:4:9
  |
4 |     use std::slice::Iter;
  |         ^^^^^^^^^^^^^^^^

warning: variable does not need to be mutable
   --> src/lib.rs:366:21
    |
366 |                 let mut curr_room = match (&mut nodes_to_visit).pop_front(){
    |                     ----^^^^^^^^^
    |                     |
    |                     help: remove this `mut`
    |
    = note: `#[warn(unused_mut)]` on by default

warning: unused `Result` that must be used
   --> src/lib.rs:234:29
    |
234 | ...                   dungeon.add_room(room_name.as_str());
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `#[warn(unused_must_use)]` on by default
    = note: this `Result` may be an `Err` variant, which should be handled

warning: `solution` (lib) generated 5 warnings
    Finished test [unoptimized + debuginfo] target(s) in 4.37s
     Running tests/solution_test.rs (target/debug/deps/solution_test-2e292b23ac75572c)

running 15 tests
test solution_test::test_adding_rooms_1 ... ok
test solution_test::test_adding_rooms_2 ... ok
test solution_test::test_cyrillic_room_names ... ok
test solution_test::test_finding_a_direct_path ... ok
test solution_test::test_finding_a_reflexive_path ... ok
test solution_test::test_finding_an_indirect_path ... ok
test solution_test::test_finding_no_path ... ok
test solution_test::test_invalid_parsing ... FAILED
test solution_test::test_io_error ... ok
test solution_test::test_overwriting_a_room_link ... ok
test solution_test::test_parsing_cyrillic_rooms ... ok
test solution_test::test_parsing_no_rooms_or_links ... ok
test solution_test::test_parsing_rooms ... FAILED
test solution_test::test_room_errors ... ok
test solution_test::test_room_links ... ok

failures:

---- solution_test::test_invalid_parsing stdout ----
thread 'main' panicked at 'assertion failed: matches!(Dungeon :: from_reader(TEST_INPUT_6.trim().as_bytes()),\n         Err(Errors :: UnknownRoom(_)))', tests/solution_test.rs:279:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- solution_test::test_parsing_rooms stdout ----
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: LineParseError { line_number: 7 }', tests/solution_test.rs:212:72


failures:
    solution_test::test_invalid_parsing
    solution_test::test_parsing_rooms

test result: FAILED. 13 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--test solution_test'

История (1 версия и 0 коментара)

Даниел качи първо решение на 10.01.2022 21:48 (преди над 3 години)