relay-knowledge 1.1.9

Graph-database-based knowledge graph project.
Documentation
use crate::domain::CodeImportRecord;

use super::super::import_resolution::{
    ImportContext, ImportResolution, ModuleFileResolution, normalize_join, parent_dir,
    parse_quoted_specifier,
};

pub(in crate::code::identity) fn resolve_import(
    import: &CodeImportRecord,
    context: &ImportContext<'_>,
) -> Option<(ImportResolution, Option<String>)> {
    let statement = import.module.trim().trim_end_matches(';').trim();
    let IncludeRequest {
        candidates,
        allow_source_root_match,
    } = parse_include(&import.path, statement)?;

    Some(
        match context.resolve_first_module_file(&candidates, allow_source_root_match) {
            ModuleFileResolution::Resolved(target_hint) => {
                (ImportResolution::Resolved, Some(target_hint))
            }
            ModuleFileResolution::Ambiguous => (ImportResolution::Ambiguous, None),
            ModuleFileResolution::Unresolved => (ImportResolution::Unresolved, None),
        },
    )
}

struct IncludeRequest {
    candidates: Vec<String>,
    allow_source_root_match: bool,
}

fn parse_include(import_path: &str, statement: &str) -> Option<IncludeRequest> {
    if !statement.starts_with("#include") {
        return None;
    }
    let (target, quoted) = if let Some(target) = parse_quoted_specifier(statement) {
        (target, true)
    } else {
        (parse_angle_specifier(statement)?, false)
    };
    let mut candidates = Vec::new();
    if quoted {
        if let Some(relative) = normalize_join(parent_dir(import_path), target) {
            candidates.push(relative);
        }
    }
    push_candidate(&mut candidates, target.to_owned());
    if !target.starts_with("include/") {
        push_candidate(&mut candidates, format!("include/{target}"));
    }

    Some(IncludeRequest {
        candidates,
        allow_source_root_match: quoted,
    })
}

fn push_candidate(candidates: &mut Vec<String>, candidate: String) {
    if !candidates.contains(&candidate) {
        candidates.push(candidate);
    }
}

fn parse_angle_specifier(statement: &str) -> Option<&str> {
    let start = statement.find('<')?;
    let rest = &statement[start + 1..];
    let end = rest.find('>')?;

    Some(&rest[..end])
}