Closed
Description
brian@ip-10-145-43-250:/mnt2/dev/strcursor⟫ rustc +nightly -Vv
rustc 1.17.0-nightly (be760566c 2017-02-28)
binary: rustc
commit-hash: be760566cf938d11d34c2f6bd90d8fd0f67c2344
commit-date: 2017-02-28
host: x86_64-unknown-linux-gnu
release: 1.17.0-nightly
LLVM version: 3.9
The source is hard to acquire so here's an equivalent test case:
mod list {
use task::Task;
use std::fmt;
pub struct List {
pub tasks: Vec<Task>,
}
impl List {
pub fn to_plaintext(&mut self) -> String {
self.tasks.sort();
self.tasks.iter()
.map(|task| task.to_plaintext())
.collect::<Vec<_>>()
.join("\n")
}
}
impl fmt::Display for List {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut list_string = String::new();
list_string.push_str("List(\n");
for task in self.tasks.iter() {
list_string.push_str(format!("{}", task).as_str());
}
list_string.push_str("\n)");
write!(f, "{}", list_string)
}
}
#[cfg(test)]
mod tests {
use super::*;
use task::*;
#[test]
fn test_list_to_plaintext() {
let mut list = List {
tasks: vec![
Task { name: "done".to_string(), state: TaskState::Done },
Task { name: "blocked".to_string(), state: TaskState::Blocked },
Task { name: "backlog".to_string(), state: TaskState::Backlog },
Task { name: "current".to_string(), state: TaskState::Current },
],
};
// Should sort and return in correct order
let expected_list_str =
"~ current\n\
- backlog\n\
= blocked\n\
+ done";
assert_eq!(list.to_plaintext(), expected_list_str);
}
}
}
mod task {
use std::cmp::Ordering;
use std::error::Error;
use std::fmt;
#[derive(Debug,PartialEq)]
pub enum TaskError {
InvalidState(char),
Malformed,
}
impl fmt::Display for TaskError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let description = self.description();
match *self {
TaskError::InvalidState(token) => write!(
f,
"{}: {}",
description,
token),
TaskError::Malformed => write!(
f,
"{}",
description),
}
}
}
impl Error for TaskError {
fn description(&self) -> &str {
match *self {
TaskError::InvalidState(_) => "Invalid token for task state",
TaskError::Malformed => "Malformed definition",
}
}
fn cause(&self) -> Option<&Error> {
match *self {
TaskError::InvalidState(_) => None,
TaskError::Malformed => None,
}
}
}
#[derive(Debug,Eq,PartialEq,PartialOrd)]
pub enum TaskState {
Current,
Backlog,
Blocked,
Done,
}
impl TaskState {
pub fn from_char(c: char) -> Result<TaskState, TaskError> {
match c {
'~' => Result::Ok(TaskState::Current),
'-' => Result::Ok(TaskState::Backlog),
'=' => Result::Ok(TaskState::Blocked),
'+' => Result::Ok(TaskState::Done),
token @ _ => Result::Err(TaskError::InvalidState(token))
}
}
pub fn to_char(&self) -> char {
match *self {
TaskState::Current => '~',
TaskState::Backlog => '-',
TaskState::Blocked => '=',
TaskState::Done => '+',
}
}
fn order_value(&self) -> u8 {
match *self {
TaskState::Current => 0,
TaskState::Backlog => 1,
TaskState::Blocked => 2,
TaskState::Done => 3,
}
}
}
impl Ord for TaskState {
fn cmp(&self, other: &TaskState) -> Ordering {
self.order_value().cmp(&other.order_value())
}
}
#[derive(Debug,Eq,PartialEq,PartialOrd)]
pub struct Task {
pub name: String,
pub state: TaskState,
}
impl Task {
pub fn new(task_str: &str) -> Result<Task, TaskError> {
let mut lines = task_str.lines();
if lines.clone().count() == 0 {
return Result::Err(TaskError::Malformed)
}
// First line should always be the task name
let main_line = lines.next().unwrap();
let mut main_line_chars = main_line.chars();
// First character must be symbol for state
let state = try!(TaskState::from_char(main_line_chars.next().unwrap()));
let mut passed_leading_whitespace = false;
let mut name = String::new();
for next_char in main_line_chars {
// Skip the leading whitespace
if !passed_leading_whitespace {
if next_char.is_whitespace() {
continue;
} else {
passed_leading_whitespace = true;
}
}
name.push(next_char)
}
Result::Ok(Task {
name: name,
state: state,
})
}
pub fn to_plaintext(&self) -> String {
format!("{} {}", self.state.to_char(), self.name)
}
}
impl fmt::Display for Task {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Task(state: {}, name: {})", self.state.to_char(), self.name)
}
}
impl Ord for Task {
fn cmp(&self, other: &Task) -> Ordering {
let state_order = self.state.cmp(&other.state);
match state_order {
Ordering::Equal => self.name.cmp(&other.name),
_ => state_order,
}
}
}
#[cfg(test)]
mod tests {
use std::cmp::Ordering;
use std::collections::HashMap;
use super::*;
#[test]
fn test_task_state_to_char() {
assert_eq!(TaskState::Current.to_char(), '~');
assert_eq!(TaskState::Backlog.to_char(), '-');
assert_eq!(TaskState::Blocked.to_char(), '=');
assert_eq!(TaskState::Done.to_char(), '+');
}
#[test]
fn test_task_state_from_char() {
assert_eq!(TaskState::from_char('~').unwrap(), TaskState::Current);
assert_eq!(TaskState::from_char('-').unwrap(), TaskState::Backlog);
assert_eq!(TaskState::from_char('=').unwrap(), TaskState::Blocked);
assert_eq!(TaskState::from_char('+').unwrap(), TaskState::Done);
assert!(TaskState::from_char('!').is_err());
}
#[test]
fn test_task_state_ord() {
assert_eq!(TaskState::Current.cmp(&TaskState::Backlog), Ordering::Less);
assert_eq!(TaskState::Backlog.cmp(&TaskState::Blocked), Ordering::Less);
assert_eq!(TaskState::Blocked.cmp(&TaskState::Done), Ordering::Less);
}
#[test]
fn test_task_new_states() {
let mut valid_tasks = HashMap::new();
valid_tasks.insert("~ current task", Task {
name: "current task".to_string(),
state: TaskState::Current,
});
valid_tasks.insert("- backlog task", Task {
name: "backlog task".to_string(),
state: TaskState::Backlog,
});
valid_tasks.insert("= blocked task", Task {
name: "blocked task".to_string(),
state: TaskState::Blocked,
});
valid_tasks.insert("+ done task", Task {
name: "done task".to_string(),
state: TaskState::Done,
});
for (task_str, task) in valid_tasks {
assert_eq!(Task::new(task_str).unwrap(), task);
}
let mut invalid_tasks = HashMap::new();
invalid_tasks.insert("! task name", TaskError::InvalidState('!'));
invalid_tasks.insert("", TaskError::Malformed);
for (task_str, task) in invalid_tasks {
assert_eq!(Task::new(task_str).err().unwrap(), task);
}
}
#[test]
fn test_task_to_plaintext() {
let task = Task {
name: "task name".to_string(),
state: TaskState::Current,
};
assert_eq!(task.to_plaintext(), "~ task name");
}
#[test]
fn test_task_ord() {
let current_task = Task { name: "current".to_string(), state: TaskState::Current };
let backlog_task = Task { name: "backlog".to_string(), state: TaskState::Backlog };
let blocked_task = Task { name: "blocked".to_string(), state: TaskState::Blocked };
let done_task = Task { name: "done".to_string(), state: TaskState::Done };
assert_eq!(current_task.cmp(&backlog_task), Ordering::Less);
assert_eq!(backlog_task.cmp(&blocked_task), Ordering::Less);
assert_eq!(blocked_task.cmp(&done_task), Ordering::Less);
}
}
}
brian@ip-10-145-43-250:~/dev⟫ ./test
running 7 tests
test task::tests::test_task_ord ... ok
test task::tests::test_task_new_states ... ok
test task::tests::test_task_state_from_char ... ok
test list::tests::test_list_to_plaintext ... FAILED
test task::tests::test_task_state_ord ... ok
test task::tests::test_task_state_to_char ... ok
test task::tests::test_task_to_plaintext ... ok
failures:
---- list::tests::test_list_to_plaintext stdout ----
thread 'list::tests::test_list_to_plaintext' panicked at 'assertion failed: `(left == right)` (left: `"- backlog\n= blocked\n~ current\n+ done"`, right: `"~ current\n- backlog\n= blocked\n+ done"`)', test.rs:55
note: Run with `RUST_BACKTRACE=1` for a backtrace.
failures:
list::tests::test_list_to_plaintext
test result: FAILED. 6 passed; 1 failed; 0 ignored; 0 measured
Not on 1.16.