relay-knowledge 1.1.16

Graph-database-based knowledge graph project.
Documentation
use std::collections::{BTreeMap, BTreeSet};

use super::mounts::{DYNAMIC_EXPRESS_MOUNT_PREFIX, ExpressRouterMount};
use super::registrations::ExpressRouteInfo;
use super::syntax::merge_url_parts;
use crate::code::parser::routes::detect::RouteCandidate;

pub(super) fn materialize_express_routes(
    route_infos: Vec<ExpressRouteInfo>,
    mounts: &[ExpressRouterMount],
    root_receiver_names: &BTreeSet<String>,
) -> Vec<RouteCandidate> {
    let router_prefixes = resolved_express_router_prefixes(mounts);
    let mut routes = Vec::new();
    let mut seen = BTreeSet::new();
    for route_info in route_infos {
        let prefixes = router_prefixes
            .get(&route_info.receiver_name)
            .cloned()
            .unwrap_or_else(|| {
                express_unmounted_receiver_prefixes(&route_info.receiver_name, root_receiver_names)
            });
        for prefix in prefixes {
            if prefix == DYNAMIC_EXPRESS_MOUNT_PREFIX {
                continue;
            }
            let url = merge_url_parts(&prefix, &route_info.local_url);
            let key = (
                url.clone(),
                route_info.http_method.clone(),
                route_info.handler_name.clone(),
                route_info.line,
            );
            if seen.insert(key) {
                routes.push(RouteCandidate {
                    url,
                    http_method: route_info.http_method.clone(),
                    handler_name: route_info.handler_name.clone(),
                    framework: "express".to_owned(),
                    line: route_info.line,
                });
            }
        }
    }
    routes
}

fn express_unmounted_receiver_prefixes(
    receiver_name: &str,
    root_receiver_names: &BTreeSet<String>,
) -> BTreeSet<String> {
    if root_receiver_names.contains(receiver_name) {
        return BTreeSet::from([String::new()]);
    }
    BTreeSet::from(["/:mount".to_owned()])
}

fn resolved_express_router_prefixes(
    mounts: &[ExpressRouterMount],
) -> BTreeMap<String, BTreeSet<String>> {
    let mounted_routers = mounts
        .iter()
        .map(|mount| mount.router_name.clone())
        .collect::<BTreeSet<_>>();
    let mut router_prefixes = BTreeMap::<String, BTreeSet<String>>::new();
    for _ in 0..=mounts.len() {
        let mut changed = false;
        for mount in mounts {
            let Some(receiver_prefixes) =
                express_receiver_prefixes(&mount.receiver_name, &router_prefixes, &mounted_routers)
            else {
                continue;
            };
            for receiver_prefix in receiver_prefixes {
                let prefix = if receiver_prefix == DYNAMIC_EXPRESS_MOUNT_PREFIX
                    || mount.local_prefix == DYNAMIC_EXPRESS_MOUNT_PREFIX
                {
                    DYNAMIC_EXPRESS_MOUNT_PREFIX.to_owned()
                } else {
                    merge_url_parts(&receiver_prefix, &mount.local_prefix)
                };
                if router_prefixes
                    .entry(mount.router_name.clone())
                    .or_default()
                    .insert(prefix)
                {
                    changed = true;
                }
            }
        }
        if !changed {
            break;
        }
    }
    router_prefixes
}

fn express_receiver_prefixes(
    receiver_name: &str,
    router_prefixes: &BTreeMap<String, BTreeSet<String>>,
    mounted_routers: &BTreeSet<String>,
) -> Option<BTreeSet<String>> {
    if let Some(prefixes) = router_prefixes.get(receiver_name) {
        return Some(prefixes.clone());
    }
    (!mounted_routers.contains(receiver_name)).then(|| BTreeSet::from([String::new()]))
}

#[cfg(test)]
#[path = "materialize_tests.rs"]
mod tests;