relay-knowledge 1.1.17

Graph-database-based knowledge graph project.
Documentation
use serde_json::Value;

use crate::domain::{
    CodeIndexMode, CodeIndexRequest, CodeMonorepoWorkspaceFormat, CodeWorkspaceDetectionConfig,
    FreshnessPolicy,
};

use super::super::{WebError, code_selector};
use crate::interfaces::code_index_mode::{mode_for_index_ref, selector_for_index_request};

pub(in crate::interfaces::web) fn code_index_request(
    payload: &Value,
    mode: CodeIndexMode,
) -> Result<CodeIndexRequest, WebError> {
    let repository = code_selector(payload)?;
    let mode = if mode == CodeIndexMode::Full {
        mode_for_index_ref(&repository.ref_selector)
    } else {
        mode
    };
    Ok(CodeIndexRequest {
        repository: selector_for_index_request(repository, &mode),
        mode,
        workspace_detection: workspace_detection_config(payload)?,
        freshness_policy: FreshnessPolicy::AllowStale,
        reuse_historical: reuse_historical(payload)?,
    })
}

fn reuse_historical(payload: &Value) -> Result<bool, WebError> {
    match payload.get("reuse_historical") {
        None | Some(Value::Null) => Ok(false),
        Some(Value::Bool(enabled)) => Ok(*enabled),
        Some(_) => Err(WebError::bad_request(
            "reuse_historical must be a boolean".to_owned(),
        )),
    }
}

fn workspace_detection_config(payload: &Value) -> Result<CodeWorkspaceDetectionConfig, WebError> {
    let Some(value) = payload.get("workspace_detection") else {
        return Ok(CodeWorkspaceDetectionConfig::default());
    };
    if value.is_null() {
        return Ok(CodeWorkspaceDetectionConfig::default());
    }
    let Some(object) = value.as_object() else {
        return Err(WebError::bad_request(
            "workspace_detection must be an object".to_owned(),
        ));
    };

    let enabled = match object.get("enabled") {
        Some(value) => value.as_bool().ok_or_else(|| {
            WebError::bad_request("workspace_detection.enabled must be a boolean".to_owned())
        })?,
        None => false,
    };
    let supported_formats = match object.get("supported_formats") {
        Some(Value::Array(items)) => items
            .iter()
            .map(|item| {
                item.as_str()
                    .ok_or_else(|| {
                        WebError::bad_request(
                            "workspace_detection.supported_formats contains a non-string value"
                                .to_owned(),
                        )
                    })
                    .and_then(parse_workspace_format)
            })
            .collect::<Result<Vec<_>, _>>()?,
        Some(_) => {
            return Err(WebError::bad_request(
                "workspace_detection.supported_formats must be an array".to_owned(),
            ));
        }
        None => CodeWorkspaceDetectionConfig::enabled_all().supported_formats,
    };

    Ok(CodeWorkspaceDetectionConfig {
        enabled,
        supported_formats,
    })
}

fn parse_workspace_format(value: &str) -> Result<CodeMonorepoWorkspaceFormat, WebError> {
    match value {
        "pnpm" => Ok(CodeMonorepoWorkspaceFormat::Pnpm),
        "go_modules" => Ok(CodeMonorepoWorkspaceFormat::GoModules),
        "cargo_workspace" => Ok(CodeMonorepoWorkspaceFormat::CargoWorkspace),
        other => Err(WebError::bad_request(format!(
            "unsupported workspace_detection.supported_formats '{other}'"
        ))),
    }
}

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