#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Router<R> {
stack: Vec<R>,
}
impl<R: Clone> Router<R> {
pub fn new(initial: R) -> Self {
Self { stack: vec![initial] }
}
pub fn current(&self) -> &R {
self.stack.last().expect("router stack must never be empty")
}
pub fn push(&mut self, route: R) {
self.stack.push(route);
}
pub fn pop(&mut self) -> bool {
if self.stack.len() > 1 {
self.stack.pop();
true
} else {
false
}
}
pub fn replace(&mut self, route: R) {
*self.stack.last_mut().expect("router stack must never be empty") = route;
}
pub fn len(&self) -> usize {
self.stack.len()
}
pub fn is_empty(&self) -> bool {
self.stack.is_empty()
}
pub fn stack(&self) -> &[R] {
&self.stack
}
pub fn push_uri<F>(&mut self, uri: &str, parser: F) -> bool
where
F: FnOnce(&str) -> Option<R>,
{
if let Some(route) = parser(uri) {
self.push(route);
true
} else {
false
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq, Eq)]
enum TestRoute {
Home,
Settings,
User { id: String },
}
impl TestRoute {
fn from_uri(uri: &str) -> Option<Self> {
match uri {
"/" | "/home" => Some(TestRoute::Home),
"/settings" => Some(TestRoute::Settings),
s if s.starts_with("/user/") => Some(TestRoute::User {
id: s.trim_start_matches("/user/").to_string(),
}),
_ => None,
}
}
}
#[test]
fn test_router_initial_state() {
let router = Router::new(TestRoute::Home);
assert_eq!(router.current(), &TestRoute::Home);
assert_eq!(router.len(), 1);
assert!(!router.is_empty());
}
#[test]
fn test_router_push() {
let mut router = Router::new(TestRoute::Home);
router.push(TestRoute::Settings);
assert_eq!(router.current(), &TestRoute::Settings);
assert_eq!(router.len(), 2);
}
#[test]
fn test_router_pop() {
let mut router = Router::new(TestRoute::Home);
router.push(TestRoute::Settings);
assert!(router.pop());
assert_eq!(router.current(), &TestRoute::Home);
assert_eq!(router.len(), 1);
assert!(!router.pop());
assert_eq!(router.current(), &TestRoute::Home);
}
#[test]
fn test_router_replace() {
let mut router = Router::new(TestRoute::Home);
router.replace(TestRoute::Settings);
assert_eq!(router.current(), &TestRoute::Settings);
assert_eq!(router.len(), 1);
}
#[test]
fn test_router_push_uri() {
let mut router = Router::new(TestRoute::Home);
assert!(router.push_uri("/settings", TestRoute::from_uri));
assert_eq!(router.current(), &TestRoute::Settings);
assert!(router.push_uri("/user/42", TestRoute::from_uri));
assert_eq!(router.current(), &TestRoute::User { id: "42".to_string() });
assert!(!router.push_uri("/invalid", TestRoute::from_uri));
assert_eq!(router.current(), &TestRoute::User { id: "42".to_string() });
}
}