rust-web-server 17.62.0

A dependency-minimal Rust web platform: HTTP/1.1, HTTP/2, and HTTP/3 server, reverse proxy, and application framework with routing, middleware (auth, rate limiting, tracing), an async ORM, background jobs, object storage, and a mailer. Runs as a zero-code config-driven proxy or as a library crate. No third-party HTTP dependencies.
Documentation
//! Path-pattern parsing and segment matching shared by [`super::Router`] and
//! `AsyncAppWithState` (`crate::async_state`).
//!
//! `AsyncAppWithState`'s handlers return `Future`s, which `Router`'s
//! `HandlerFn` type doesn't support, so it can't reuse `Router` itself — but
//! the underlying pattern-matching algorithm is identical, so it lives here
//! once instead of being duplicated.

use std::collections::HashMap;

#[derive(Clone)]
pub(crate) enum Segment {
    Literal(String),
    Param(String),
    Wildcard(String),
}

pub(crate) fn parse_pattern(pattern: &str) -> Vec<Segment> {
    if pattern == "/" {
        return vec![];
    }
    pattern
        .split('/')
        .filter(|s| !s.is_empty())
        .map(|seg| {
            if let Some(name) = seg.strip_prefix(':') {
                Segment::Param(name.to_string())
            } else if let Some(name) = seg.strip_prefix('*') {
                Segment::Wildcard(name.to_string())
            } else {
                Segment::Literal(seg.to_string())
            }
        })
        .collect()
}

/// Matches `path` (already split on `/`, empty segments filtered out)
/// against `pattern`. Returns the extracted named/wildcard values on a match.
pub(crate) fn try_match(pattern: &[Segment], path: &[&str]) -> Option<HashMap<String, String>> {
    let mut params = HashMap::new();
    let mut pi = 0;

    for (si, seg) in pattern.iter().enumerate() {
        match seg {
            Segment::Literal(lit) => {
                if pi >= path.len() || path[pi] != lit.as_str() {
                    return None;
                }
                pi += 1;
            }
            Segment::Param(name) => {
                if pi >= path.len() {
                    return None;
                }
                params.insert(name.clone(), path[pi].to_string());
                pi += 1;
            }
            Segment::Wildcard(name) => {
                if si != pattern.len() - 1 {
                    return None; // wildcard must be the last segment
                }
                params.insert(name.clone(), path[pi..].join("/"));
                pi = path.len();
            }
        }
    }

    if pi == path.len() { Some(params) } else { None }
}

/// Reconstructs the original `.get()`/`.post()`/etc. pattern string from
/// parsed segments — the inverse of [`parse_pattern`]. Used by both
/// `Router::route_entries` and `AsyncAppWithState::route_entries`.
pub(crate) fn segments_to_pattern(segs: &[Segment]) -> String {
    if segs.is_empty() {
        return "/".to_string();
    }
    let parts: Vec<String> = segs
        .iter()
        .map(|s| match s {
            Segment::Literal(l) => l.clone(),
            Segment::Param(n) => format!(":{}", n),
            Segment::Wildcard(n) => format!("*{}", n),
        })
        .collect();
    format!("/{}", parts.join("/"))
}