use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SegmentType {
Static,
Dynamic,
Wildcard,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RouteSegment {
raw: String,
segment_type: SegmentType,
}
impl RouteSegment {
pub fn parse_path(path: &str) -> Vec<Self> {
let mut segments = Vec::new();
let path = path.trim_start_matches('/');
let path = path.trim_end_matches('/');
if path.is_empty() {
return segments;
}
for part in path.split('/') {
let segment_type = if part == "*" {
SegmentType::Wildcard
} else if part.starts_with(':') {
SegmentType::Dynamic
} else {
SegmentType::Static
};
segments.push(RouteSegment {
raw: part.to_string(),
segment_type,
});
}
segments
}
pub fn segment_type(&self) -> SegmentType {
self.segment_type
}
pub fn is_dynamic(&self) -> bool {
self.segment_type == SegmentType::Dynamic
}
pub fn is_wildcard(&self) -> bool {
self.segment_type == SegmentType::Wildcard
}
pub fn param_name(&self) -> Option<&str> {
if self.segment_type == SegmentType::Dynamic {
Some(&self.raw[1..]) } else {
None
}
}
pub fn matches(&self, segment: &str) -> bool {
match self.segment_type {
SegmentType::Static => self.raw == segment,
SegmentType::Dynamic => true, SegmentType::Wildcard => true, }
}
}
impl fmt::Display for RouteSegment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.raw)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_empty_path() {
let segments = RouteSegment::parse_path("/");
assert!(segments.is_empty());
}
#[test]
fn test_parse_simple_path() {
let segments = RouteSegment::parse_path("/users");
assert_eq!(segments.len(), 1);
assert_eq!(segments[0].raw, "users");
assert_eq!(segments[0].segment_type, SegmentType::Static);
}
#[test]
fn test_parse_nested_path() {
let segments = RouteSegment::parse_path("/users/posts");
assert_eq!(segments.len(), 2);
assert_eq!(segments[0].raw, "users");
assert_eq!(segments[1].raw, "posts");
}
#[test]
fn test_parse_dynamic_path() {
let segments = RouteSegment::parse_path("/users/:id");
assert_eq!(segments.len(), 2);
assert_eq!(segments[0].raw, "users");
assert_eq!(segments[1].raw, ":id");
assert_eq!(segments[1].segment_type, SegmentType::Dynamic);
assert_eq!(segments[1].param_name(), Some("id"));
}
#[test]
fn test_parse_wildcard_path() {
let segments = RouteSegment::parse_path("/files/*");
assert_eq!(segments.len(), 2);
assert_eq!(segments[0].raw, "files");
assert_eq!(segments[1].raw, "*");
assert_eq!(segments[1].segment_type, SegmentType::Wildcard);
}
#[test]
fn test_parse_mixed_path() {
let segments = RouteSegment::parse_path("/users/:id/posts/:post_id");
assert_eq!(segments.len(), 4);
assert_eq!(segments[0].segment_type, SegmentType::Static);
assert_eq!(segments[1].segment_type, SegmentType::Dynamic);
assert_eq!(segments[2].segment_type, SegmentType::Static);
assert_eq!(segments[3].segment_type, SegmentType::Dynamic);
}
#[test]
fn test_static_segment_matches() {
let segment = RouteSegment {
raw: "users".to_string(),
segment_type: SegmentType::Static,
};
assert!(segment.matches("users"));
assert!(!segment.matches("posts"));
}
#[test]
fn test_dynamic_segment_matches() {
let segment = RouteSegment {
raw: ":id".to_string(),
segment_type: SegmentType::Dynamic,
};
assert!(segment.matches("123"));
assert!(segment.matches("abc"));
assert!(segment.matches(""));
}
#[test]
fn test_wildcard_segment_matches() {
let segment = RouteSegment {
raw: "*".to_string(),
segment_type: SegmentType::Wildcard,
};
assert!(segment.matches("anything"));
}
}