dhomer 0.1.0

Simple and easy to use, a proxy server based on Pingora
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use serde::Serialize;
use super::Result;
use crate::config::error::Error;

#[derive(Hash, PartialEq, Eq, Debug, Serialize, Clone)]
pub struct RouterPattern{
    segments: Vec<String>,
}

impl RouterPattern {
    pub fn new<S: AsRef<str> + Into<String>>(s: S) -> Result<Self> {
        let segments = s.as_ref().split("/")
            .filter(|s| !s.is_empty())
            .map(|s| s.into())
            .collect();

        Ok(Self{ segments })
    }

    pub fn is_match<S: AsRef<str>>(&self, other: S) -> bool {
        let mut other_segments = other.as_ref()
            .split('/')
            .filter(|s| !s.is_empty());

        for s in &self.segments {
            let Some(n) = other_segments.next() else {
                return false;
            };

            if s != "*" && s != n {
                return false;
            }
        }

        true
    }

    pub fn rest_after_match<S: AsRef<str>>(&self, other: S) -> Option<String> {
        let mut other_segments = other.as_ref()
            .split('/')
            .filter(|s| !s.is_empty());

        for s in &self.segments {
            let Some(n) = other_segments.next() else {
                return None;
            };

            if s != "*" && s != n {
                return None;
            }
        }

        let remaining = other_segments.fold(String::from("/"), |mut acc, segment| {
            if !acc.is_empty() {
                acc.push('/');
            }
            acc.push_str(segment);

            acc
        });

        Some(remaining)
    }
}

impl From<RouterPattern> for String {
    fn from(value: RouterPattern) -> Self {
        format!("/{}", value.segments.join("/"))
    }
}

impl Display for RouterPattern {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "/{}", self.segments.join("/"))
    }
}

impl TryFrom<String> for RouterPattern {
    type Error = Error;

    fn try_from(value: String) -> std::result::Result<Self, Self::Error> {
        Self::new(value)
    }
}

impl TryFrom<&str> for RouterPattern {
    type Error = Error;

    fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
        Self::new(value)
    }
}

impl FromStr for RouterPattern {
    type Err = Error;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        RouterPattern::try_from(s)
    }
}