mercur 0.1.6

mercur - junolang package manager
use std::collections::HashSet;

use crate::registry::{
    schema::registry::PkgRegistryEntry,
    search::{SearchEngine, SearchResult},
};

struct SearchNode {
    registry_ref: String,
    depth: usize,
}

impl SearchEngine {
    pub async fn search_in_registry(
        &mut self,
        registry_ref: &str,
        namespaces: Vec<&str>,
        package: &str,
        authority: Option<&str>,
        version: Option<&str>,
        hash: Option<&str>,
        out: &mut Vec<SearchResult>,
    ) -> anyhow::Result<()> {
        let mut visited = HashSet::new();
        let mut stack = Vec::new();

        stack.push(SearchNode {
            registry_ref: registry_ref.to_owned(),
            depth: 0,
        });

        while let Some(node) = stack.pop() {
            if !visited.insert(node.registry_ref.clone()) {
                continue;
            }

            let registry = self.load_registry(&node.registry_ref).await?;
            if node.depth < namespaces.len() {
                let wanted = namespaces[node.depth];

                if let Some(authorities) = registry.authorities() {
                    for authority in authorities {
                        for reg in authority.registries() {
                            if reg.namespace() == wanted {
                                stack.push(SearchNode {
                                    registry_ref: reg.ref_().to_owned(),

                                    depth: node.depth + 1,
                                });
                            }
                        }
                    }
                }

                continue;
            }
            if let Some(packages) = registry.packages() {
                for pkg in packages {
                    if match_pkg(&pkg, package, authority, version, hash) {
                        out.push(SearchResult::from_pkg_registry_entry(
                            pkg,
                            &node.registry_ref,
                        ));
                    }
                }
            }
        }

        Ok(())
    }
}

fn match_pkg(
    x: &PkgRegistryEntry<'_>,
    package: &str,
    authority: Option<&str>,
    version: Option<&str>,
    hash: Option<&str>,
) -> bool {
    if x.name() != package {
        return false;
    }

    if let Some(authority) = authority {
        if x.authority() != authority {
            return false;
        }
    }

    if let Some(version) = version {
        let mut found = false;

        if let Some(versions) = x.versions() {
            for v in versions {
                let normal = format!("{}.{}.{}", v.major(), v.minor(), v.patch());

                let tagged = format!(
                    "{}.{}.{}-{}",
                    v.major(),
                    v.minor(),
                    v.patch(),
                    v.tag().unwrap_or("")
                );

                if version == normal || version == tagged {
                    found = true;
                    break;
                }
            }
        }

        if !found {
            return false;
        }
    }

    if let Some(hash) = hash {
        if let Some(versions) = x.versions() {
            for v in versions {
                if let Some(h) = v.hash() {
                    let current =
                        format!("{:016x}{:016x}{:016x}{:016x}", h.a(), h.b(), h.c(), h.d());

                    if current == hash {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    true
}