use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum OutputFormat {
#[default]
Yaml,
Json,
}
impl OutputFormat {
pub fn from_string(s: &str) -> Self {
match s {
"json" => Self::Json,
_ => Self::Yaml,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScanConfig {
pub path: PathBuf,
pub format: OutputFormat,
#[serde(default = "default_include_patterns")]
pub include: Vec<String>,
#[serde(default = "default_exclude_patterns")]
pub exclude: Vec<String>,
#[serde(default = "default_parallel")]
pub parallel: bool,
#[serde(default)]
pub max_threads: Option<usize>,
#[serde(default)]
pub incremental: bool,
#[serde(default = "default_cache_dir")]
pub cache_dir: PathBuf,
#[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 {
pub fn new(path: impl Into<PathBuf>) -> Self {
Self {
path: path.into(),
..Default::default()
}
}
pub fn with_path(mut self, path: impl Into<PathBuf>) -> Self {
self.path = path.into();
self
}
pub fn with_format(mut self, format: impl AsRef<str>) -> Self {
self.format = match format.as_ref() {
"json" => OutputFormat::Json,
_ => OutputFormat::Yaml,
};
self
}
pub fn with_include(mut self, pattern: impl Into<String>) -> Self {
self.include.push(pattern.into());
self
}
pub fn with_exclude(mut self, pattern: impl Into<String>) -> Self {
self.exclude.push(pattern.into());
self
}
pub fn with_parallel(mut self, parallel: bool) -> Self {
self.parallel = parallel;
self
}
pub fn with_max_threads(mut self, threads: usize) -> Self {
self.max_threads = Some(threads);
self
}
pub fn with_incremental(mut self, incremental: bool) -> Self {
self.incremental = incremental;
self
}
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);
}
}