relay-knowledge 1.1.10

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

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

use super::{WebError, code_selector};

pub(in crate::interfaces) fn code_index_request(
    payload: &Value,
    mode: CodeIndexMode,
) -> Result<CodeIndexRequest, WebError> {
    Ok(CodeIndexRequest {
        repository: code_selector(payload)?,
        mode,
        workspace_detection: workspace_detection_config(payload)?,
        freshness_policy: FreshnessPolicy::AllowStale,
    })
}

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)]
mod tests {
    use serde_json::json;

    use super::*;
    use crate::domain::{CodeRepositorySelector, CodeWorkspaceDetectionConfig};

    fn payload() -> Value {
        json!({
            "alias": "relay",
            "ref": "main",
            "path_filters": [],
            "language_filters": []
        })
    }

    #[test]
    fn defaults_workspace_detection_when_absent() {
        let request = code_index_request(&payload(), CodeIndexMode::Full).expect("request");
        assert_eq!(
            request.workspace_detection,
            CodeWorkspaceDetectionConfig::default()
        );
    }

    #[test]
    fn parses_workspace_detection_config() {
        let mut payload = payload();
        payload["workspace_detection"] = json!({
            "enabled": true,
            "supported_formats": ["pnpm", "go_modules"]
        });

        let request = code_index_request(&payload, CodeIndexMode::Full).expect("request");

        assert!(request.workspace_detection.enabled);
        assert_eq!(
            request.workspace_detection.supported_formats,
            vec![
                CodeMonorepoWorkspaceFormat::Pnpm,
                CodeMonorepoWorkspaceFormat::GoModules,
            ]
        );
        assert_eq!(
            request.repository,
            CodeRepositorySelector::new("relay", "main", Vec::new(), Vec::new()).expect("selector")
        );
    }

    #[test]
    fn rejects_unsupported_workspace_detection_format() {
        let mut payload = payload();
        payload["workspace_detection"] = json!({
            "enabled": true,
            "supported_formats": ["unknown"]
        });

        let error = code_index_request(&payload, CodeIndexMode::Full).expect_err("error");
        assert!(
            error
                .message
                .contains("unsupported workspace_detection.supported_formats")
        );
    }
}