raxit-core 0.1.2

Core security scanning engine for AI agent applications
Documentation
//! Configuration types for RAXIT scanning

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

/// Output format for scan results
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum OutputFormat {
    #[default]
    Yaml,
    Json,
}

impl OutputFormat {
    /// Get format from string
    pub fn from_string(s: &str) -> Self {
        match s {
            "json" => Self::Json,
            _ => Self::Yaml,
        }
    }
}

/// Configuration for RAXIT scan
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScanConfig {
    /// Path to scan (directory or file)
    pub path: PathBuf,

    /// Output format (yaml or json)
    pub format: OutputFormat,

    /// File patterns to include (glob patterns)
    #[serde(default = "default_include_patterns")]
    pub include: Vec<String>,

    /// File patterns to exclude (glob patterns)
    #[serde(default = "default_exclude_patterns")]
    pub exclude: Vec<String>,

    /// Enable parallel processing
    #[serde(default = "default_parallel")]
    pub parallel: bool,

    /// Maximum number of threads for parallel processing
    #[serde(default)]
    pub max_threads: Option<usize>,

    /// Enable incremental scanning with cache
    #[serde(default)]
    pub incremental: bool,

    /// Cache directory for incremental scanning
    #[serde(default = "default_cache_dir")]
    pub cache_dir: PathBuf,

    /// Enable verbose output
    #[serde(default)]
    pub verbose: bool,
}

fn default_include_patterns() -> Vec<String> {
    vec!["**/*.py".to_string()]
}

fn default_exclude_patterns() -> Vec<String> {
    vec![
        "**/node_modules/**".to_string(),
        "**/.venv/**".to_string(),
        "**/__pycache__/**".to_string(),
        "**/dist/**".to_string(),
        "**/build/**".to_string(),
        "**/.git/**".to_string(),
    ]
}

fn default_parallel() -> bool {
    true
}

fn default_cache_dir() -> PathBuf {
    PathBuf::from(".raxit/cache")
}

impl Default for ScanConfig {
    fn default() -> Self {
        Self {
            path: PathBuf::from("."),
            format: OutputFormat::Yaml,
            include: default_include_patterns(),
            exclude: default_exclude_patterns(),
            parallel: default_parallel(),
            max_threads: None,
            incremental: false,
            cache_dir: default_cache_dir(),
            verbose: false,
        }
    }
}

impl ScanConfig {
    /// Create a new config with the given path
    pub fn new(path: impl Into<PathBuf>) -> Self {
        Self {
            path: path.into(),
            ..Default::default()
        }
    }

    /// Set the scan path
    pub fn with_path(mut self, path: impl Into<PathBuf>) -> Self {
        self.path = path.into();
        self
    }

    /// Set the output format
    pub fn with_format(mut self, format: impl AsRef<str>) -> Self {
        self.format = match format.as_ref() {
            "json" => OutputFormat::Json,
            _ => OutputFormat::Yaml,
        };
        self
    }

    /// Add include pattern
    pub fn with_include(mut self, pattern: impl Into<String>) -> Self {
        self.include.push(pattern.into());
        self
    }

    /// Add exclude pattern
    pub fn with_exclude(mut self, pattern: impl Into<String>) -> Self {
        self.exclude.push(pattern.into());
        self
    }

    /// Enable/disable parallel processing
    pub fn with_parallel(mut self, parallel: bool) -> Self {
        self.parallel = parallel;
        self
    }

    /// Set max threads
    pub fn with_max_threads(mut self, threads: usize) -> Self {
        self.max_threads = Some(threads);
        self
    }

    /// Enable incremental scanning
    pub fn with_incremental(mut self, incremental: bool) -> Self {
        self.incremental = incremental;
        self
    }

    /// Enable verbose output
    pub fn with_verbose(mut self, verbose: bool) -> Self {
        self.verbose = verbose;
        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_default_config() {
        let config = ScanConfig::default();
        assert_eq!(config.path, PathBuf::from("."));
        assert_eq!(config.format, OutputFormat::Yaml);
        assert!(config.parallel);
    }

    #[test]
    fn test_builder_pattern() {
        let config = ScanConfig::default()
            .with_path("/my/project")
            .with_format("json")
            .with_parallel(false);

        assert_eq!(config.path, PathBuf::from("/my/project"));
        assert_eq!(config.format, OutputFormat::Json);
        assert!(!config.parallel);
    }
}