sark-gen 0.28.7

Sark proc-macro generators
Documentation
use core::cmp;
use std::collections;

use crate::app::route;

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct Path<'a> {
    index: usize,
    segments: &'a [route::Seg],
    literals: usize,
}

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
struct StateKey<'a> {
    depth: usize,
    routes: Vec<Path<'a>>,
}

pub(super) struct State {
    pub(super) depth: usize,
    pub(super) accept: Vec<usize>,
    pub(super) literals: collections::BTreeMap<Vec<u8>, usize>,
    pub(super) fallback: Option<usize>,
}

#[derive(Default)]
pub(super) struct Machine {
    pub(super) starts: collections::BTreeMap<route::Method, usize>,
    pub(super) states: Vec<State>,
    pub(super) asterisk: collections::BTreeMap<route::Method, Vec<usize>>,
}

impl Machine {
    pub(super) fn build(plan: &route::Plan) -> Self {
        let mut asterisk = collections::BTreeMap::<route::Method, Vec<usize>>::new();
        let mut keys = collections::BTreeMap::<StateKey<'_>, usize>::new();
        let mut queue = collections::VecDeque::<StateKey<'_>>::new();
        let mut paths = collections::BTreeMap::<route::Method, Vec<Path<'_>>>::new();
        for (index, entry) in plan.routes.iter().enumerate() {
            match entry.target.path() {
                Some(segments) => paths.entry(entry.method).or_default().push(Path {
                    index,
                    segments,
                    literals: entry.literal_count,
                }),
                None => asterisk.entry(entry.method).or_default().push(index),
            }
        }
        let mut initial = Vec::with_capacity(paths.len());
        for (method, routes) in paths {
            let key = StateKey { depth: 0, routes };
            let id = Self::intern(key, &mut keys, &mut queue);
            initial.push((method, id));
        }
        let starts = initial.into_iter().collect();

        let mut states = Vec::new();
        while let Some(key) = queue.pop_front() {
            let mut accept = key
                .routes
                .iter()
                .copied()
                .filter(|path| path.segments.len() == key.depth)
                .collect::<Vec<_>>();
            accept.sort_by_key(|path| cmp::Reverse(path.literals));
            let accept = accept.into_iter().map(|path| path.index).collect();

            let active = key
                .routes
                .iter()
                .copied()
                .filter(|path| path.segments.len() > key.depth)
                .collect::<Vec<_>>();
            let params = active
                .iter()
                .copied()
                .filter(|path| {
                    path.segments
                        .get(key.depth)
                        .is_some_and(|segment| !segment.is_literal())
                })
                .collect::<Vec<_>>();
            let fallback = if params.is_empty() {
                None
            } else {
                Some(Self::intern(
                    StateKey {
                        depth: key.depth + 1,
                        routes: params.clone(),
                    },
                    &mut keys,
                    &mut queue,
                ))
            };
            let literals = active
                .iter()
                .filter_map(|path| path.segments.get(key.depth))
                .filter_map(route::Seg::literal)
                .map(<[u8]>::to_vec)
                .collect::<collections::BTreeSet<_>>()
                .into_iter()
                .map(|literal| {
                    let mut routes = params.clone();
                    routes.extend(active.iter().copied().filter(|path| {
                        path.segments.get(key.depth).and_then(route::Seg::literal)
                            == Some(literal.as_slice())
                    }));
                    routes.sort_unstable();
                    let next = Self::intern(
                        StateKey {
                            depth: key.depth + 1,
                            routes,
                        },
                        &mut keys,
                        &mut queue,
                    );
                    (literal, next)
                })
                .collect();
            states.push(State {
                depth: key.depth,
                accept,
                literals,
                fallback,
            });
        }
        Self {
            starts,
            states,
            asterisk,
        }
    }

    fn intern<'a>(
        key: StateKey<'a>,
        keys: &mut collections::BTreeMap<StateKey<'a>, usize>,
        queue: &mut collections::VecDeque<StateKey<'a>>,
    ) -> usize {
        use collections::btree_map::Entry;

        let id = keys.len();
        match keys.entry(key.clone()) {
            Entry::Occupied(slot) => *slot.get(),
            Entry::Vacant(slot) => {
                let id = *slot.insert(id);
                queue.push_back(key);
                id
            }
        }
    }
}