relay-knowledge 1.0.7

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

use super::imports::{ImportContext, ImportResolution, ModuleFileResolution};

pub(super) fn resolve_import(
    import: &CodeImportRecord,
    context: &ImportContext<'_>,
) -> Option<(ImportResolution, Option<String>)> {
    let request = JavaImportRequest::parse(&import.module)?;

    Some(match request {
        JavaImportRequest::Class { class_path, name } => {
            let file_path = java_source_path(&class_path);
            match context.resolve_first_module_file(std::slice::from_ref(&file_path), true) {
                ModuleFileResolution::Resolved(target_hint) => {
                    (ImportResolution::Resolved, Some(target_hint))
                }
                ModuleFileResolution::Ambiguous => (ImportResolution::Ambiguous, None),
                ModuleFileResolution::Unresolved => {
                    (context.resolve_name_in_paths(&name, &[file_path]), None)
                }
            }
        }
        JavaImportRequest::PackageWildcard { package_path } => {
            if context.directory_has_language_files(&package_path, "java") {
                (ImportResolution::Resolved, Some(package_path))
            } else {
                (ImportResolution::Unresolved, None)
            }
        }
        JavaImportRequest::StaticMember { class_path, member } => {
            let file_path = java_source_path(&class_path);
            (context.resolve_name_in_paths(&member, &[file_path]), None)
        }
        JavaImportRequest::StaticWildcard { class_path } => {
            let file_path = java_source_path(&class_path);
            match context.resolve_first_module_file(&[file_path], true) {
                ModuleFileResolution::Resolved(target_hint) => {
                    (ImportResolution::Resolved, Some(target_hint))
                }
                ModuleFileResolution::Ambiguous => (ImportResolution::Ambiguous, None),
                ModuleFileResolution::Unresolved => (ImportResolution::Unresolved, None),
            }
        }
    })
}

enum JavaImportRequest {
    Class { class_path: String, name: String },
    PackageWildcard { package_path: String },
    StaticMember { class_path: String, member: String },
    StaticWildcard { class_path: String },
}

impl JavaImportRequest {
    fn parse(statement: &str) -> Option<Self> {
        let body = statement
            .trim()
            .trim_end_matches(';')
            .trim()
            .strip_prefix("import ")?;
        let (is_static, body) = body
            .strip_prefix("static ")
            .map_or((false, body), |body| (true, body.trim()));
        if body.is_empty() {
            return None;
        }
        if let Some(prefix) = body.strip_suffix(".*") {
            let path = prefix.replace('.', "/");
            return if is_static {
                Some(Self::StaticWildcard { class_path: path })
            } else {
                Some(Self::PackageWildcard { package_path: path })
            };
        }

        let (parent, name) = body.rsplit_once('.')?;
        let parent_path = parent.replace('.', "/");
        let name = name.trim();
        if name.is_empty() {
            return None;
        }
        if is_static {
            Some(Self::StaticMember {
                class_path: parent_path,
                member: name.to_owned(),
            })
        } else {
            Some(Self::Class {
                class_path: format!("{parent_path}/{name}"),
                name: name.to_owned(),
            })
        }
    }
}

fn java_source_path(class_path: &str) -> String {
    format!("{class_path}.java")
}