use std::error;
use std::fmt;
use std::io::{self, Write};
use std::iter;
use std::string::FromUtf8Error;
#[derive(Clone, Debug)]
pub enum Event<'a> {
StartTrack(usize),
StartTracks(&'a [usize]),
StopTrack(usize),
Station(usize, &'a str),
SplitTrack(usize, usize),
JoinTrack(usize, usize),
NoEvent,
}
pub fn to_writer<W: Write>(mut writer: W, events: &[Event]) -> Result<(), Error> {
let mut tracks = vec![0];
for event in events {
use Event::*;
match event {
&StartTrack(track_id) => {
if !tracks.contains(&track_id) {
tracks.push(track_id);
let line = iter::repeat("|")
.take(tracks.len())
.collect::<Vec<_>>()
.join(" ");
writeln!(&mut writer, "{}", line)?;
}
}
&StartTracks(track_ids) => {
let mut render = false;
for track_id in track_ids.iter() {
if !tracks.contains(track_id) {
tracks.push(*track_id);
render = true;
}
}
if render {
let line = iter::repeat("|")
.take(tracks.len())
.collect::<Vec<_>>()
.join(" ");
writeln!(&mut writer, "{}", line)?;
}
}
&StopTrack(track_id) => stop_track(&mut writer, &mut tracks, track_id)?,
&Station(track_id, station_name) => {
let line = tracks
.iter()
.map(|&id| if id == track_id { "*" } else { "|" })
.collect::<Vec<_>>()
.join(" ");
writeln!(&mut writer, "{} {}", line, station_name)?;
}
&SplitTrack(from_track_id, new_track_id) => {
if !tracks.contains(&new_track_id) {
let from_track_index = tracks.iter().position(|&id| id == from_track_id);
if let Some(from_track_index) = from_track_index {
let line = (0..tracks.len())
.map(|i| {
use std::cmp::Ordering::*;
match i.cmp(&from_track_index) {
Greater => "\\",
Equal => "|\\",
Less => "|",
}
})
.collect::<Vec<_>>()
.join(" ");
writeln!(&mut writer, "{}", line)?;
tracks.insert(from_track_index + 1, new_track_id);
} else {
tracks.push(new_track_id);
let line = iter::repeat("|")
.take(tracks.len())
.collect::<Vec<_>>()
.join(" ");
writeln!(&mut writer, "{}", line)?;
}
}
}
&JoinTrack(from_track_id, to_track_id) => {
let from_track_index = tracks.iter().position(|&id| id == from_track_id);
if from_track_id == to_track_id {
stop_track(&mut writer, &mut tracks, from_track_id)?;
continue;
}
if let Some(from_track_index) = from_track_index {
let to_track_index = tracks.iter().position(|&id| id == to_track_id);
if let Some(to_track_index) = to_track_index {
let left_index = from_track_index.min(to_track_index);
let right_index = from_track_index.max(to_track_index);
if (right_index - left_index) == 1 {
let line = (0..tracks.len())
.filter_map(|i| {
if i > right_index {
Some("/")
} else if i == left_index {
Some("|/")
} else if i != right_index {
Some("|")
} else {
None
}
})
.collect::<Vec<_>>()
.join(" ");
writeln!(&mut writer, "{}", line)?;
} else {
let line = (0..tracks.len())
.filter_map(|i| {
if i > right_index {
Some(" /")
} else if i == right_index {
None
} else if i >= (right_index - 1) {
Some("|/")
} else if i > left_index {
Some("|_")
} else {
Some("| ")
}
})
.collect::<Vec<_>>()
.concat();
writeln!(&mut writer, "{}", line)?;
let track_count = tracks.len() - 1;
let line = (0..track_count)
.map(|i| {
if i == left_index {
"|/"
} else if i == (track_count - 1) {
"|"
} else {
"| "
}
})
.collect::<Vec<_>>()
.concat();
writeln!(&mut writer, "{}", line)?;
}
tracks.remove(from_track_index);
} else {
stop_track(&mut writer, &mut tracks, from_track_id)?;
}
}
}
NoEvent => {
let line = iter::repeat("|")
.take(tracks.len())
.collect::<Vec<_>>()
.join(" ");
writeln!(&mut writer, "{}", line)?;
}
}
}
Ok(())
}
fn stop_track<W: Write>(
mut writer: W,
tracks: &mut Vec<usize>,
track_id: usize,
) -> Result<(), Error> {
if let Some(index) = tracks.iter().position(|&id| id == track_id) {
let line = (0..tracks.len())
.map(|i| if i == index { "\"" } else { "|" })
.collect::<Vec<_>>()
.join(" ");
writeln!(&mut writer, "{}", line)?;
if index != (tracks.len() - 1) {
let line = (0..tracks.len())
.map(|i| {
use std::cmp::Ordering::*;
match i.cmp(&index) {
Greater => "/",
Equal => "",
Less => "|",
}
})
.collect::<Vec<_>>()
.join(" ");
writeln!(&mut writer, "{}", line)?;
}
tracks.remove(index);
}
Ok(())
}
#[inline]
pub fn to_vec(events: &[Event]) -> Result<Vec<u8>, Error> {
let mut vec = Vec::new();
to_writer(&mut vec, events)?;
Ok(vec)
}
#[inline]
pub fn to_string(events: &[Event]) -> Result<String, Error> {
let vec = to_vec(events)?;
Ok(String::from_utf8(vec)?)
}
#[derive(Debug)]
pub enum Error {
IoError(io::Error),
FromUtf8Error(FromUtf8Error),
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
match self {
IoError(err) => err.fmt(fmt),
FromUtf8Error(err) => err.fmt(fmt),
}
}
}
impl error::Error for Error {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
use Error::*;
match self {
IoError(ref err) => Some(err),
FromUtf8Error(ref err) => Some(err),
}
}
}
impl From<io::Error> for Error {
#[inline]
fn from(err: io::Error) -> Self {
Self::IoError(err)
}
}
impl From<FromUtf8Error> for Error {
#[inline]
fn from(err: FromUtf8Error) -> Self {
Self::FromUtf8Error(err)
}
}
#[cfg(test)]
mod tests {
use super::to_string;
use super::Event::*;
#[test]
fn start_track() {
let events = [StartTrack(1)];
let string = to_string(&events).unwrap();
assert_eq!(string, "| |\n");
}
#[test]
fn start_track_already_exists() {
let events = [StartTrack(0)];
let string = to_string(&events).unwrap();
assert_eq!(string, "");
}
#[test]
fn start_track_already_exists2() {
#[rustfmt::skip]
let events = [
StartTrack(1),
StartTrack(2),
StartTrack(1),
StartTrack(2),
];
let string = to_string(&events).unwrap();
assert_eq!(string, "| |\n| | |\n");
}
#[test]
fn start_track_default() {
let events = [StartTrack(0)];
let string = to_string(&events).unwrap();
assert_eq!(string, "");
}
#[test]
fn event_start_track_default2() {
let events = [StartTrack(1)];
let string = to_string(&events).unwrap();
assert_eq!(string, "| |\n");
}
#[test]
fn event_start_tracks() {
let events = [StartTracks(&[1, 2, 3])];
let string = to_string(&events).unwrap();
assert_eq!(string, "| | | |\n");
}
#[test]
fn start_tracks_some_already_exist() {
let events = [StartTracks(&[0, 1, 2])];
let string = to_string(&events).unwrap();
assert_eq!(string, "| | |\n");
}
#[test]
fn start_tracks_all_already_exist() {
let events = [StartTracks(&[0, 0, 0])];
let string = to_string(&events).unwrap();
assert_eq!(string, "");
}
#[test]
fn stop_track() {
let events = [StopTrack(0)];
let string = to_string(&events).unwrap();
assert_eq!(string, "\"\n");
}
#[test]
fn stop_track_does_not_exist() {
let events = [StopTrack(1)];
let string = to_string(&events).unwrap();
assert_eq!(string, "");
}
#[test]
fn stop_track_left() {
#[rustfmt::skip]
let events = [
StartTracks(&[0, 1, 2, 3, 4]),
StopTrack(0),
NoEvent,
];
let string = to_string(&events).unwrap();
assert_eq!(
string,
r#"| | | | |
" | | | |
/ / / /
| | | |
"#
);
}
#[test]
fn stop_track_middle() {
#[rustfmt::skip]
let events = [
StartTracks(&[0, 1, 2, 3, 4]),
StopTrack(2),
NoEvent,
];
let string = to_string(&events).unwrap();
assert_eq!(
string,
r#"| | | | |
| | " | |
| | / /
| | | |
"#
);
}
#[test]
fn stop_track_right() {
#[rustfmt::skip]
let events = [
StartTracks(&[0, 1, 2, 3, 4]),
StopTrack(4),
NoEvent,
];
let string = to_string(&events).unwrap();
assert_eq!(
string,
r#"| | | | |
| | | | "
| | | |
"#
);
}
#[test]
fn station() {
let events = [
StartTracks(&[0, 1, 2]),
Station(0, "Station 1"),
Station(1, "Station 2"),
Station(2, "Station 3"),
Station(0, "Station 4"),
Station(1, "Station 5"),
Station(2, "Station 6"),
];
let string = to_string(&events).unwrap();
assert_eq!(
string,
r#"| | |
* | | Station 1
| * | Station 2
| | * Station 3
* | | Station 4
| * | Station 5
| | * Station 6
"#
);
}
#[test]
fn station_non_existing_track() {
let events = [
StartTracks(&[0, 1, 2]),
Station(0, "Station 1"),
Station(1, "Station 2"),
Station(2, "Station 3"),
Station(3, "Station 4"),
Station(4, "Station 5"),
Station(5, "Station 6"),
Station(std::usize::MAX, "Station 7"),
];
let string = to_string(&events).unwrap();
assert_eq!(
string,
r#"| | |
* | | Station 1
| * | Station 2
| | * Station 3
| | | Station 4
| | | Station 5
| | | Station 6
| | | Station 7
"#
);
}
#[test]
fn split_track() {
let events = [
SplitTrack(0, 1),
NoEvent,
SplitTrack(0, 2),
SplitTrack(1, 3),
SplitTrack(3, 4),
];
let string = to_string(&events).unwrap();
assert_eq!(
string,
r#"|\
| |
|\ \
| | |\
| | | |\
"#
);
}
#[test]
fn split_track_non_existing_from_track() {
let events1 = [
SplitTrack(1, 2),
SplitTrack(3, 4),
Station(2, "2"),
Station(4, "4"),
];
let events2 = [
StartTrack(2),
StartTrack(4),
Station(2, "2"),
Station(4, "4"),
];
let string1 = to_string(&events1).unwrap();
let string2 = to_string(&events2).unwrap();
assert_eq!(string1, string2);
}
#[test]
fn split_track_already_existing_new_track() {
let events1 = [
StartTracks(&[0, 1, 2]),
SplitTrack(0, 1),
SplitTrack(0, 2),
SplitTrack(3, 4),
Station(0, "0"),
Station(1, "1"),
Station(2, "2"),
Station(3, "3"),
Station(4, "4"),
];
let events2 = [
StartTracks(&[0, 1, 2]),
StartTrack(4),
Station(0, "0"),
Station(1, "1"),
Station(2, "2"),
Station(3, "3"),
Station(4, "4"),
];
let string1 = to_string(&events1).unwrap();
let string2 = to_string(&events2).unwrap();
assert_eq!(string1, string2);
}
#[test]
fn split_track_same_from_and_new_track() {
let events = [SplitTrack(0, 0)];
let string = to_string(&events).unwrap();
assert_eq!(string, "");
#[rustfmt::skip]
let events1 = [
StartTracks(&[0, 1, 2]),
SplitTrack(1, 1),
SplitTrack(0, 2),
];
let events2 = [StartTracks(&[0, 1, 2])];
let string1 = to_string(&events1).unwrap();
let string2 = to_string(&events2).unwrap();
assert_eq!(string1, string2);
}
#[test]
fn join_track_zero_between() {
#[rustfmt::skip]
let events = [
StartTracks(&[0, 1, 2]),
JoinTrack(1, 0),
NoEvent,
];
let string = to_string(&events).unwrap();
assert_eq!(
string,
r#"| | |
|/ /
| |
"#
);
}
#[test]
fn join_track_one_between() {
#[rustfmt::skip]
let events = [
StartTracks(&[0, 1, 2]),
JoinTrack(2, 0),
NoEvent,
];
let string = to_string(&events).unwrap();
assert_eq!(
string,
r#"| | |
| |/
|/|
| |
"#
);
}
#[test]
fn join_track_many_between() {
#[rustfmt::skip]
let events = [
StartTracks(&[0, 1, 2, 3, 4]),
JoinTrack(4, 0),
NoEvent,
];
let string = to_string(&events).unwrap();
assert_eq!(
string,
r#"| | | | |
| |_|_|/
|/| | |
| | | |
"#
);
}
#[test]
fn join_track_always_leftmost() {
let events1 = [
StartTracks(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
JoinTrack(4, 1),
JoinTrack(5, 0),
JoinTrack(8, 7),
JoinTrack(9, 6),
NoEvent,
];
let events2 = events1
.iter()
.cloned()
.map(|event| match event {
JoinTrack(from, to) => JoinTrack(to, from),
_ => event,
})
.collect::<Vec<_>>();
let string1 = to_string(&events1).unwrap();
let string2 = to_string(&events2).unwrap();
assert_eq!(string1, string2);
}
#[test]
fn join_track_non_existing_from_track_existing_to_track() {
let events1 = [
StartTracks(&[0, 1, 2, 3, 4]),
JoinTrack(5, 0),
JoinTrack(10, 1),
];
let events2 = [StartTracks(&[0, 1, 2, 3, 4])];
let string1 = to_string(&events1).unwrap();
let string2 = to_string(&events2).unwrap();
assert_eq!(string1, string2);
}
#[test]
fn join_track_non_existing_from_track_non_existing_to_track() {
let events1 = [
StartTracks(&[0, 1, 2, 3, 4]),
JoinTrack(5, 6),
JoinTrack(10, 11),
];
let events2 = [StartTracks(&[0, 1, 2, 3, 4])];
let string1 = to_string(&events1).unwrap();
let string2 = to_string(&events2).unwrap();
assert_eq!(string1, string2);
}
#[test]
fn join_track_existing_from_track_non_existing_to_track() {
let events1 = [
StartTracks(&[0, 1, 2, 3, 4]),
JoinTrack(0, 5),
JoinTrack(2, 10),
];
#[rustfmt::skip]
let events2 = [
StartTracks(&[0, 1, 2, 3, 4]),
StopTrack(0),
StopTrack(2),
];
let string1 = to_string(&events1).unwrap();
let string2 = to_string(&events2).unwrap();
assert_eq!(string1, string2);
}
#[test]
fn join_track_same_from_and_to_track() {
let events1 = [
StartTracks(&[0, 1, 2, 3, 4]),
JoinTrack(0, 0),
JoinTrack(2, 2),
JoinTrack(10, 10),
];
#[rustfmt::skip]
let events2 = [
StartTracks(&[0, 1, 2, 3, 4]),
StopTrack(0),
StopTrack(2),
];
let string1 = to_string(&events1).unwrap();
let string2 = to_string(&events2).unwrap();
assert_eq!(string1, string2);
}
#[test]
fn no_event() {
let events = [NoEvent, NoEvent, NoEvent];
let string = to_string(&events).unwrap();
assert_eq!(string, "|\n|\n|\n");
#[rustfmt::skip]
let events = [
StartTracks(&[0, 1, 2]),
NoEvent,
NoEvent,
NoEvent,
];
let string = to_string(&events).unwrap();
assert_eq!(string, "| | |\n| | |\n| | |\n| | |\n");
}
}