hyperlane-quick-start 23.0.46

A lightweight rust http server with middleware, websocket, sse, and tcp support, built on tokio for cross-platform async networking, hyperlane simplifies modern web service development.
use std::{
    collections::HashSet,
    fs,
    path::{Path, PathBuf},
};

const DEPENDENCY_BLOCKS: [&str; 4] = [
    "dependencies",
    "dev-dependencies",
    "build-dependencies",
    "workspace.dependencies",
];

struct Entry {
    key: String,
    length: usize,
    blank_after: bool,
}

fn manifests(root: &Path) -> Vec<PathBuf> {
    let mut found: Vec<PathBuf> = Vec::new();
    let mut stack: Vec<PathBuf> = vec![root.to_path_buf()];
    while let Some(directory) = stack.pop() {
        let Ok(entries) = fs::read_dir(&directory) else {
            continue;
        };
        for entry in entries.flatten() {
            let path: PathBuf = entry.path();
            let name: String = entry.file_name().to_string_lossy().to_string();
            if name == "target" || name == ".git" || name == "node_modules" {
                continue;
            }
            if path.is_dir() {
                stack.push(path);
                continue;
            }
            if name == "Cargo.toml" {
                found.push(path);
            }
        }
    }
    found.sort();
    found
}

fn package_name(text: &str) -> Option<String> {
    let mut in_package: bool = false;
    for line in text.lines() {
        let trimmed: &str = line.trim();
        if trimmed.starts_with('[') {
            in_package = trimmed == "[package]";
            continue;
        }
        if in_package
            && let Some(value) = trimmed.strip_prefix("name")
            && let Some(name) = value.split('=').nth(1)
        {
            return Some(name.trim().trim_matches('"').to_string());
        }
    }
    None
}

fn workspace_members(root: &Path, text: &str) -> Vec<String> {
    let mut names: Vec<String> = Vec::new();
    let mut members: Vec<String> = Vec::new();
    let mut in_members: bool = false;
    for line in text.lines() {
        let trimmed: &str = line.trim();
        if trimmed.starts_with('[') {
            in_members = trimmed == "[workspace]";
            continue;
        }
        if in_members && let Some(value) = trimmed.strip_prefix("members") {
            let raw: &str = value.split('=').nth(1).unwrap_or_default();
            for part in raw.split(',') {
                let member: &str = part.trim().trim_matches('"');
                if !member.is_empty() {
                    members.push(member.to_string());
                }
            }
        }
    }
    for member in members {
        let manifest: PathBuf = root.join(&member).join("Cargo.toml");
        if let Ok(text) = fs::read_to_string(&manifest)
            && let Some(name) = package_name(&text)
        {
            names.push(name);
        }
    }
    names
}

fn block_entries(text: &str, block: &str) -> Vec<Entry> {
    let mut entries: Vec<Entry> = Vec::new();
    let mut inside: bool = false;
    let mut pending: Option<(String, usize, isize)> = None;
    for line in text.lines() {
        let trimmed: &str = line.trim();
        if trimmed.starts_with('[') {
            let name: &str = trimmed.trim_start_matches('[').trim_end_matches(']');
            if inside {
                break;
            }
            inside = name == block;
            continue;
        }
        if !inside {
            continue;
        }
        if trimmed.is_empty() {
            match pending.take() {
                Some((key, length, _)) => entries.push(Entry {
                    key,
                    length,
                    blank_after: true,
                }),
                None => {
                    if let Some(entry) = entries.last_mut() {
                        entry.blank_after = true;
                    }
                }
            }
            continue;
        }
        let opens: isize = trimmed.chars().filter(|c: &char| *c == '{').count() as isize;
        let closes: isize = trimmed.chars().filter(|c: &char| *c == '}').count() as isize;
        if let Some((_, length, depth)) = pending.as_mut() {
            *length += trimmed.chars().count();
            *depth += opens;
            *depth -= closes;
            if *depth <= 0 {
                let (key, length, _) = pending.take().expect("the entry is there");
                entries.push(Entry {
                    key,
                    length,
                    blank_after: false,
                });
            }
            continue;
        }
        let key: String = trimmed
            .split('=')
            .next()
            .unwrap_or_default()
            .trim()
            .to_string();
        let depth: isize = opens - closes;
        if depth <= 0 {
            entries.push(Entry {
                key,
                length: trimmed.chars().count(),
                blank_after: false,
            });
        } else {
            pending = Some((key, trimmed.chars().count(), depth));
        }
    }
    entries
}

fn check_manifest(path: &Path, local: &HashSet<String>) -> Vec<String> {
    let mut problems: Vec<String> = Vec::new();
    let Ok(text) = fs::read_to_string(path) else {
        return problems;
    };
    for block in DEPENDENCY_BLOCKS {
        let entries: Vec<Entry> = block_entries(&text, block);
        if entries.is_empty() {
            continue;
        }
        let mut local_entries: Vec<&Entry> = entries
            .iter()
            .filter(|entry: &&Entry| local.contains(&entry.key))
            .collect();
        let mut third: Vec<&Entry> = entries
            .iter()
            .filter(|entry: &&Entry| !local.contains(&entry.key))
            .collect();
        let boundary: usize = if local_entries.is_empty() || third.is_empty() {
            0
        } else {
            1
        };
        let blanks: usize = entries
            .iter()
            .take(entries.len().saturating_sub(1))
            .filter(|entry: &&Entry| entry.blank_after)
            .count();
        if blanks != boundary {
            problems.push(format!(
                "{} [{block}] order: {blanks} blank lines inside the block, expected {boundary}",
                path.display()
            ));
        }
        local_entries.sort_by(|first: &&Entry, second: &&Entry| {
            (first.length, first.key.as_str()).cmp(&(second.length, second.key.as_str()))
        });
        third.sort_by(|first: &&Entry, second: &&Entry| {
            (first.length, first.key.as_str()).cmp(&(second.length, second.key.as_str()))
        });
        let expected: Vec<&str> = local_entries
            .iter()
            .chain(third.iter())
            .map(|entry: &&Entry| entry.key.as_str())
            .collect();
        let actual: Vec<&str> = entries
            .iter()
            .map(|entry: &Entry| entry.key.as_str())
            .collect();
        if expected != actual {
            problems.push(format!(
                "{} [{block}] order: expected {expected:?}, found {actual:?}",
                path.display()
            ));
        }
    }
    problems
}

#[test]
fn dependency_blocks_keep_their_order() {
    let root: PathBuf = Path::new(env!("CARGO_MANIFEST_DIR")).join("frontend");
    if !root.join("Cargo.toml").is_file() {
        return;
    }
    let mut local: HashSet<String> = HashSet::new();
    for manifest in manifests(&root) {
        let Ok(text) = fs::read_to_string(&manifest) else {
            continue;
        };
        if let Some(name) = package_name(&text) {
            local.insert(name);
        }
        for member in workspace_members(&root, &text) {
            local.insert(member);
        }
    }
    let mut problems: Vec<String> = Vec::new();
    for manifest in manifests(&root) {
        problems.extend(check_manifest(&manifest, &local));
    }
    assert!(
        problems.is_empty(),
        "the dependency blocks of the frontend workspace are out of order:\n{}",
        problems.join("\n")
    );
}