securegit 0.8.5

Zero-trust git replacement with 12 built-in security scanners, LLM redteam bridge, universal undo, durable backups, and a 50-tool MCP server
Documentation
//! GraphRAG configuration
//!
//! Loads settings from environment variables and config files.

use serde::{Deserialize, Serialize};
use std::path::PathBuf;

/// GraphRAG service configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphRagConfig {
    /// API endpoint URL (default: http://localhost:7474)
    pub api_url: String,

    /// Whether GraphRAG indexing is enabled
    pub enabled: bool,

    /// Timeout for API requests in seconds
    pub timeout_secs: u64,

    /// Whether to use incremental indexing (only changed files)
    pub incremental: bool,

    /// Whether to block on indexing completion or run async
    pub async_index: bool,

    /// Path mapping from host to container (format: "host_prefix:container_prefix")
    /// Example: "/home/alice/projects:/workspace"
    pub path_map: Option<String>,

    /// API key for authenticating with the GraphRAG service
    pub api_key: Option<String>,
}

impl Default for GraphRagConfig {
    fn default() -> Self {
        Self {
            api_url: std::env::var("GRAPHRAG_API_URL")
                .unwrap_or_else(|_| "http://localhost:7474".to_string()),
            enabled: std::env::var("GRAPHRAG_ENABLED")
                .map(|v| v == "1" || v.to_lowercase() == "true")
                .unwrap_or(false),
            timeout_secs: std::env::var("GRAPHRAG_TIMEOUT")
                .ok()
                .and_then(|v| v.parse().ok())
                .unwrap_or(30),
            incremental: std::env::var("GRAPHRAG_INCREMENTAL")
                .map(|v| v != "0" && v.to_lowercase() != "false")
                .unwrap_or(true),
            async_index: std::env::var("GRAPHRAG_ASYNC")
                .map(|v| v == "1" || v.to_lowercase() == "true")
                .unwrap_or(true),
            path_map: std::env::var("GRAPHRAG_PATH_MAP").ok(),
            api_key: std::env::var("GRAPHRAG_API_KEY").ok(),
        }
    }
}

impl GraphRagConfig {
    /// Translate a local path to the container path using path_map
    /// Example: with path_map="/home/alice/projects:/workspace"
    ///          "/home/alice/projects/myapp" -> "/workspace/myapp"
    pub fn translate_path(&self, local_path: &str) -> String {
        if let Some(ref mapping) = self.path_map {
            if let Some((host_prefix, container_prefix)) = mapping.split_once(':') {
                if local_path.starts_with(host_prefix) {
                    return local_path.replacen(host_prefix, container_prefix, 1);
                }
            }
        }
        local_path.to_string()
    }

    /// Maximum config file size (1 MB)
    const MAX_CONFIG_SIZE: u64 = 1024 * 1024;

    /// Load config from a file if it exists
    pub fn load_from_file(path: &PathBuf) -> Option<Self> {
        if path.exists() {
            let meta = std::fs::metadata(path).ok()?;
            if meta.len() > Self::MAX_CONFIG_SIZE {
                return None;
            }
            let content = std::fs::read_to_string(path).ok()?;
            toml::from_str(&content).ok()
        } else {
            None
        }
    }

    /// Load config with precedence: env vars > config file > defaults
    pub fn load() -> Self {
        // Check for config file in standard locations
        let config_paths = [
            dirs::config_dir().map(|p| p.join("securegit/graphrag.toml")),
            Some(PathBuf::from(".securegit/graphrag.toml")),
        ];

        let mut config = Self::default();

        for path_opt in config_paths.iter().flatten() {
            if let Some(file_config) = Self::load_from_file(path_opt) {
                // File config overrides defaults, but env vars override file
                if std::env::var("GRAPHRAG_API_URL").is_err() {
                    config.api_url = file_config.api_url;
                }
                if std::env::var("GRAPHRAG_ENABLED").is_err() {
                    config.enabled = file_config.enabled;
                }
                if std::env::var("GRAPHRAG_TIMEOUT").is_err() {
                    config.timeout_secs = file_config.timeout_secs;
                }
                if std::env::var("GRAPHRAG_INCREMENTAL").is_err() {
                    config.incremental = file_config.incremental;
                }
                if std::env::var("GRAPHRAG_ASYNC").is_err() {
                    config.async_index = file_config.async_index;
                }
                if std::env::var("GRAPHRAG_PATH_MAP").is_err() {
                    config.path_map = file_config.path_map;
                }
                if std::env::var("GRAPHRAG_API_KEY").is_err() {
                    config.api_key = file_config.api_key;
                }
                break;
            }
        }

        config
    }
}