use std::cell::RefCell;
use std::fmt;
use std::io::Write;
use std::mem;
use std::rc::Rc;
use crate::events::{to_string, to_vec, to_writer, Error, Event};
type RcMetro<'a> = Rc<RefCell<MetroState<'a>>>;
#[allow(missing_debug_implementations)]
pub struct Metro<'a> {
state: RcMetro<'a>,
}
impl<'a> Metro<'a> {
#[allow(clippy::new_without_default)]
#[inline]
pub fn new() -> Self {
Self {
state: Rc::new(RefCell::new(MetroState::new())),
}
}
#[inline]
pub fn new_track(&mut self) -> Track<'a> {
let id = self.state.borrow_mut().next_id();
self.new_track_with_id(id)
}
#[inline]
pub fn new_track_with_id(&mut self, track_id: usize) -> Track<'a> {
MetroState::new_track(&self.state, track_id)
}
#[inline]
pub fn get_track(&mut self, track_id: usize) -> Option<Track<'a>> {
MetroState::get_track(&self.state, track_id)
}
#[inline]
pub fn add_station(&mut self, text: &'a str) {
MetroState::add_event(&self.state, Event::Station(std::usize::MAX, text));
}
#[inline]
pub fn to_writer<W: Write>(&self, writer: W) -> Result<(), Error> {
let state = self.state.borrow();
to_writer(writer, &state.events)
}
#[inline]
pub fn to_vec(&self) -> Result<Vec<u8>, Error> {
let state = self.state.borrow();
to_vec(&state.events)
}
#[inline]
pub fn to_string(&self) -> Result<String, Error> {
let state = self.state.borrow();
to_string(&state.events)
}
#[inline]
pub fn to_events(&self) -> Vec<Event<'a>> {
let state = self.state.borrow();
state.events.clone()
}
#[inline]
pub fn into_events(self) -> Vec<Event<'a>> {
let mut state = self.state.borrow_mut();
mem::replace(&mut state.events, Vec::new())
}
}
pub struct Track<'a> {
state: RcMetro<'a>,
id: usize,
}
impl<'a> Track<'a> {
#[inline]
fn new(state: RcMetro<'a>, id: usize) -> Self {
Self { state, id }
}
#[inline]
pub fn id(&self) -> usize {
self.id
}
#[inline]
pub fn stop(self) {
}
#[inline]
pub fn add_station(&mut self, text: &'a str) {
MetroState::add_event(&self.state, Event::Station(self.id, text));
}
#[inline]
pub fn split(&self) -> Track<'a> {
let id = self.state.borrow_mut().next_id();
self.split_with_id(id)
}
#[inline]
pub fn split_with_id(&self, new_track_id: usize) -> Track<'a> {
MetroState::split_track(&self.state, self, new_track_id)
}
#[inline]
pub fn join(self, to_track: &Track) {
MetroState::join_track(&self.state, &self, to_track);
}
#[inline]
pub fn is_dangling(&self) -> bool {
self.state
.borrow()
.tracks
.iter()
.all(|track| track.id != self.id)
}
#[inline]
fn clone_ref(&self) -> Self {
Self {
state: Rc::clone(&self.state),
id: self.id,
}
}
}
impl<'a> Drop for Track<'a> {
#[inline]
fn drop(&mut self) {
let is_dangling = self
.state
.try_borrow()
.map(|metro| metro.tracks.iter().all(|track| track.id != self.id))
.unwrap_or(true);
if !is_dangling {
MetroState::add_event(&self.state, Event::StopTrack(self.id));
let mut state = self.state.borrow_mut();
let index = state
.tracks
.iter()
.position(|track| track.id == self.id)
.unwrap();
state.tracks.remove(index);
}
}
}
impl fmt::Debug for Track<'_> {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Track").field("id", &self.id).finish()
}
}
struct MetroState<'a> {
tracks: Vec<Track<'a>>,
events: Vec<Event<'a>>,
next_id: usize,
}
impl<'a> MetroState<'a> {
#[inline]
fn new() -> Self {
Self {
tracks: vec![],
events: vec![],
next_id: 0,
}
}
#[inline]
fn next_id(&mut self) -> usize {
let id = self.next_id;
self.next_id += 1;
id
}
#[inline]
fn new_track(metro: &RcMetro<'a>, track_id: usize) -> Track<'a> {
let state = metro.borrow();
let track = state.tracks.iter().find(|track| track.id == track_id);
if let Some(track) = track {
track.clone_ref()
} else {
drop(state);
let track = Track::new(Rc::clone(metro), track_id);
metro.borrow_mut().tracks.push(track.clone_ref());
MetroState::add_event(metro, Event::StartTrack(track_id));
track
}
}
#[inline]
fn get_track(metro: &RcMetro<'a>, track_id: usize) -> Option<Track<'a>> {
let state = metro.borrow();
let track = state.tracks.iter().find(|track| track.id == track_id);
track.map(Track::clone_ref)
}
#[inline]
fn split_track(metro: &RcMetro<'a>, from_track: &Track, new_track_id: usize) -> Track<'a> {
let state = metro.borrow();
let new_track = state.tracks.iter().find(|track| track.id == new_track_id);
if let Some(new_track) = new_track {
new_track.clone_ref()
} else {
drop(state);
let new_track = Track::new(Rc::clone(metro), new_track_id);
metro.borrow_mut().tracks.push(new_track.clone_ref());
MetroState::add_event(metro, Event::SplitTrack(from_track.id(), new_track_id));
new_track
}
}
#[inline]
fn join_track(metro: &RcMetro<'a>, from_track: &Track, to_track: &Track) {
let from_track_id = from_track.id();
MetroState::add_event(metro, Event::JoinTrack(from_track_id, to_track.id()));
let mut state = metro.borrow_mut();
let index = state
.tracks
.iter()
.position(|track| track.id == from_track_id)
.unwrap();
state.tracks.remove(index);
}
#[inline]
fn add_event(metro: &RcMetro<'a>, event: Event<'a>) {
metro.borrow_mut().events.push(event);
}
}
#[cfg(test)]
mod tests {
use super::{to_string, Event::*, Metro};
#[test]
fn lib_example() {
let events = [
Station(0, "Station 1"),
Station(0, "Station 2"),
Station(0, "Station 3"),
SplitTrack(0, 1),
Station(1, "Station 4"),
SplitTrack(1, 2),
Station(1, "Station 5"),
Station(2, "Station 6"),
Station(0, "Station 7"),
Station(1, "Station 8"),
Station(2, "Station 9"),
SplitTrack(2, 3),
SplitTrack(3, 4),
Station(5, "Station 10 (Detached)"),
JoinTrack(4, 0),
Station(3, "Station 11"),
StopTrack(1),
Station(0, "Station 12"),
Station(2, "Station 13"),
Station(3, "Station 14"),
JoinTrack(3, 0),
Station(2, "Station 15"),
StopTrack(2),
Station(0, "Station 16"),
];
let string1 = to_string(&events).unwrap();
assert_eq!(
string1,
r#"* Station 1
* Station 2
* Station 3
|\
| * Station 4
| |\
| * | Station 5
| | * Station 6
* | | Station 7
| * | Station 8
| | * Station 9
| | |\
| | | |\
| | | | | Station 10 (Detached)
| |_|_|/
|/| | |
| | | * Station 11
| " | |
| / /
* | | Station 12
| * | Station 13
| | * Station 14
| |/
|/|
| * Station 15
| "
* Station 16
"#
);
let mut metro = Metro::new();
let mut track1 = metro.new_track();
track1.add_station("Station 1");
track1.add_station("Station 2");
track1.add_station("Station 3");
let mut track2 = track1.split();
track2.add_station("Station 4");
let mut track3 = track2.split();
track2.add_station("Station 5");
track3.add_station("Station 6");
track1.add_station("Station 7");
track2.add_station("Station 8");
track3.add_station("Station 9");
let mut track4 = track3.split();
let track5 = track4.split();
metro.add_station("Station 10 (Detached)");
track5.join(&track1);
track4.add_station("Station 11");
track2.stop();
track1.add_station("Station 12");
track3.add_station("Station 13");
track4.add_station("Station 14");
track4.join(&track1);
track3.add_station("Station 15");
track3.stop();
track1.add_station("Station 16");
let string2 = metro.to_string().unwrap();
assert_eq!(string1, string2);
}
}