1use crate::Result;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::path::{Path, PathBuf};
11use std::time::Duration;
12use tracing::info;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct CodePrismProfile {
17 pub name: String,
19 pub description: String,
21 pub settings: ServerSettings,
23 pub tools: ToolsConfig,
25 pub monitoring: MonitoringConfig,
27 pub security: SecurityConfig,
29 pub caching: CachingConfig,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct ServerSettings {
36 pub name: String,
38 pub version: String,
40 pub memory_limit_mb: usize,
42 pub batch_size: usize,
44 pub max_file_size_mb: usize,
46 pub disable_memory_limit: bool,
48 pub exclude_dirs: Vec<String>,
50 pub include_extensions: Option<Vec<String>>,
52 pub dependency_mode: DependencyMode,
54 pub default_timeout: Duration,
56 pub max_concurrent_operations: usize,
58 pub enable_streaming: bool,
60 pub max_response_size: usize,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub enum DependencyMode {
67 Exclude,
69 Smart,
71 IncludeAll,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct ToolsConfig {
78 pub enabled_categories: Vec<ToolCategory>,
80 pub disabled_tools: Vec<String>,
82 pub tool_configs: HashMap<String, ToolConfig>,
84 pub enablement_rules: Vec<EnablementRule>,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
90pub enum ToolCategory {
91 CoreNavigation,
93 SearchDiscovery,
95 Analysis,
97 Workflow,
99 Experimental,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct ToolConfig {
106 pub timeout: Option<Duration>,
108 pub max_results: Option<usize>,
110 pub memory_limit_mb: Option<usize>,
112 pub custom_params: HashMap<String, serde_json::Value>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct EnablementRule {
119 pub name: String,
121 pub condition: EnablementCondition,
123 pub actions: Vec<EnablementAction>,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
129pub enum EnablementCondition {
130 RepositorySize { max_size_mb: usize },
132 FileCount { max_files: usize },
134 HasLanguages { languages: Vec<String> },
136 ClientType { client_types: Vec<String> },
138 RepositoryType { repo_types: Vec<String> },
140 Custom { expression: String },
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub enum EnablementAction {
147 Enable { tools: Vec<String> },
149 Disable { tools: Vec<String> },
151 EnableCategory { category: ToolCategory },
153 DisableCategory { category: ToolCategory },
155 Configure { tool: String, config: ToolConfig },
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct MonitoringConfig {
162 pub enabled: bool,
164 pub collection_interval: Duration,
166 pub monitor_memory: bool,
168 pub monitor_response_times: bool,
170 pub monitor_errors: bool,
172 pub export_metrics: bool,
174 pub metrics_export_path: Option<PathBuf>,
176 pub alert_thresholds: AlertThresholds,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct AlertThresholds {
183 pub max_memory_mb: usize,
185 pub max_response_time_ms: u64,
187 pub max_error_rate: f64,
189 pub min_success_rate: f64,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct SecurityConfig {
196 pub enable_audit_log: bool,
198 pub audit_log_path: Option<PathBuf>,
200 pub allowed_paths: Vec<PathBuf>,
202 pub denied_paths: Vec<PathBuf>,
204 pub max_analysis_depth: usize,
206 pub validate_paths: bool,
208 pub rate_limiting: RateLimitConfig,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct RateLimitConfig {
215 pub enabled: bool,
217 pub requests_per_minute: usize,
219 pub max_concurrent: usize,
221 pub burst_size: usize,
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct CachingConfig {
228 pub enabled: bool,
230 pub cache_dir: PathBuf,
232 pub max_cache_size_mb: usize,
234 pub analysis_ttl: Duration,
236 pub content_ttl: Duration,
238 pub enable_compression: bool,
240 pub cleanup_interval: Duration,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct Config {
247 pub profile: CodePrismProfile,
249 #[serde(skip)]
251 pub manager: ConfigProfileManager,
252}
253
254#[derive(Debug, Clone)]
256pub struct ConfigProfileManager {
257 profiles: HashMap<String, CodePrismProfile>,
258 active_profile: Option<String>,
259}
260
261impl ConfigProfileManager {
262 pub fn new() -> Self {
264 let mut manager = Self {
265 profiles: HashMap::new(),
266 active_profile: None,
267 };
268
269 manager.register_builtin_profiles();
271 manager
272 }
273
274 fn register_builtin_profiles(&mut self) {
276 self.profiles.insert(
278 "development".to_string(),
279 CodePrismProfile {
280 name: "development".to_string(),
281 description: "Fast development with minimal resource usage".to_string(),
282 settings: ServerSettings {
283 name: crate::SERVER_NAME.to_string(),
284 version: crate::VERSION.to_string(),
285 memory_limit_mb: 1024,
286 batch_size: 10,
287 max_file_size_mb: 5,
288 disable_memory_limit: false,
289 exclude_dirs: vec![
290 "node_modules".to_string(),
291 "target".to_string(),
292 ".git".to_string(),
293 ],
294 include_extensions: Some(vec![
295 "py".to_string(),
296 "js".to_string(),
297 "ts".to_string(),
298 "rs".to_string(),
299 ]),
300 dependency_mode: DependencyMode::Exclude,
301 default_timeout: Duration::from_secs(30),
302 max_concurrent_operations: 4,
303 enable_streaming: true,
304 max_response_size: 50_000,
305 },
306 tools: ToolsConfig {
307 enabled_categories: vec![
308 ToolCategory::CoreNavigation,
309 ToolCategory::SearchDiscovery,
310 ],
311 disabled_tools: vec!["analyze_transitive_dependencies".to_string()],
312 tool_configs: HashMap::new(),
313 enablement_rules: vec![],
314 },
315 monitoring: MonitoringConfig {
316 enabled: true,
317 collection_interval: Duration::from_secs(60),
318 monitor_memory: true,
319 monitor_response_times: true,
320 monitor_errors: true,
321 export_metrics: false,
322 metrics_export_path: None,
323 alert_thresholds: AlertThresholds {
324 max_memory_mb: 2048,
325 max_response_time_ms: 10000,
326 max_error_rate: 0.1,
327 min_success_rate: 0.9,
328 },
329 },
330 security: SecurityConfig {
331 enable_audit_log: false,
332 audit_log_path: None,
333 allowed_paths: vec![],
334 denied_paths: vec![],
335 max_analysis_depth: 100,
336 validate_paths: true,
337 rate_limiting: RateLimitConfig {
338 enabled: false,
339 requests_per_minute: 100,
340 max_concurrent: 10,
341 burst_size: 20,
342 },
343 },
344 caching: CachingConfig {
345 enabled: true,
346 cache_dir: PathBuf::from("./cache/dev"),
347 max_cache_size_mb: 256,
348 analysis_ttl: Duration::from_secs(3600),
349 content_ttl: Duration::from_secs(1800),
350 enable_compression: false,
351 cleanup_interval: Duration::from_secs(3600),
352 },
353 },
354 );
355
356 self.profiles.insert(
358 "production".to_string(),
359 CodePrismProfile {
360 name: "production".to_string(),
361 description: "Production deployment with high performance and monitoring"
362 .to_string(),
363 settings: ServerSettings {
364 name: crate::SERVER_NAME.to_string(),
365 version: crate::VERSION.to_string(),
366 memory_limit_mb: 8192,
367 batch_size: 50,
368 max_file_size_mb: 25,
369 disable_memory_limit: false,
370 exclude_dirs: vec![
371 "node_modules".to_string(),
372 "target".to_string(),
373 ".git".to_string(),
374 "vendor".to_string(),
375 "dist".to_string(),
376 "build".to_string(),
377 ],
378 include_extensions: None, dependency_mode: DependencyMode::Smart,
380 default_timeout: Duration::from_secs(120),
381 max_concurrent_operations: 12,
382 enable_streaming: true,
383 max_response_size: 150_000,
384 },
385 tools: ToolsConfig {
386 enabled_categories: vec![
387 ToolCategory::CoreNavigation,
388 ToolCategory::SearchDiscovery,
389 ToolCategory::Analysis,
390 ToolCategory::Workflow,
391 ],
392 disabled_tools: vec![],
393 tool_configs: HashMap::new(),
394 enablement_rules: vec![EnablementRule {
395 name: "large_repository".to_string(),
396 condition: EnablementCondition::RepositorySize { max_size_mb: 1000 },
397 actions: vec![EnablementAction::Disable {
398 tools: vec!["find_duplicates".to_string()],
399 }],
400 }],
401 },
402 monitoring: MonitoringConfig {
403 enabled: true,
404 collection_interval: Duration::from_secs(30),
405 monitor_memory: true,
406 monitor_response_times: true,
407 monitor_errors: true,
408 export_metrics: true,
409 metrics_export_path: Some(PathBuf::from("./metrics")),
410 alert_thresholds: AlertThresholds {
411 max_memory_mb: 10240,
412 max_response_time_ms: 30000,
413 max_error_rate: 0.05,
414 min_success_rate: 0.95,
415 },
416 },
417 security: SecurityConfig {
418 enable_audit_log: true,
419 audit_log_path: Some(PathBuf::from("./logs/audit.log")),
420 allowed_paths: vec![],
421 denied_paths: vec![
422 PathBuf::from("/etc"),
423 PathBuf::from("/var"),
424 PathBuf::from("/proc"),
425 ],
426 max_analysis_depth: 1000,
427 validate_paths: true,
428 rate_limiting: RateLimitConfig {
429 enabled: true,
430 requests_per_minute: 200,
431 max_concurrent: 15,
432 burst_size: 50,
433 },
434 },
435 caching: CachingConfig {
436 enabled: true,
437 cache_dir: PathBuf::from("./cache/prod"),
438 max_cache_size_mb: 2048,
439 analysis_ttl: Duration::from_secs(7200),
440 content_ttl: Duration::from_secs(3600),
441 enable_compression: true,
442 cleanup_interval: Duration::from_secs(1800),
443 },
444 },
445 );
446
447 self.profiles.insert(
449 "enterprise".to_string(),
450 CodePrismProfile {
451 name: "enterprise".to_string(),
452 description: "Enterprise deployment with maximum performance and security"
453 .to_string(),
454 settings: ServerSettings {
455 name: crate::SERVER_NAME.to_string(),
456 version: crate::VERSION.to_string(),
457 memory_limit_mb: 16384,
458 batch_size: 100,
459 max_file_size_mb: 50,
460 disable_memory_limit: false,
461 exclude_dirs: vec![
462 "node_modules".to_string(),
463 "target".to_string(),
464 ".git".to_string(),
465 "vendor".to_string(),
466 "dist".to_string(),
467 "build".to_string(),
468 "coverage".to_string(),
469 ],
470 include_extensions: None,
471 dependency_mode: DependencyMode::Smart,
472 default_timeout: Duration::from_secs(300),
473 max_concurrent_operations: 24,
474 enable_streaming: true,
475 max_response_size: 500_000,
476 },
477 tools: ToolsConfig {
478 enabled_categories: vec![
479 ToolCategory::CoreNavigation,
480 ToolCategory::SearchDiscovery,
481 ToolCategory::Analysis,
482 ToolCategory::Workflow,
483 ],
484 disabled_tools: vec![],
485 tool_configs: HashMap::new(),
486 enablement_rules: vec![],
487 },
488 monitoring: MonitoringConfig {
489 enabled: true,
490 collection_interval: Duration::from_secs(15),
491 monitor_memory: true,
492 monitor_response_times: true,
493 monitor_errors: true,
494 export_metrics: true,
495 metrics_export_path: Some(PathBuf::from("./metrics")),
496 alert_thresholds: AlertThresholds {
497 max_memory_mb: 20480,
498 max_response_time_ms: 60000,
499 max_error_rate: 0.02,
500 min_success_rate: 0.98,
501 },
502 },
503 security: SecurityConfig {
504 enable_audit_log: true,
505 audit_log_path: Some(PathBuf::from("./logs/audit.log")),
506 allowed_paths: vec![],
507 denied_paths: vec![
508 PathBuf::from("/etc"),
509 PathBuf::from("/var"),
510 PathBuf::from("/proc"),
511 PathBuf::from("/sys"),
512 ],
513 max_analysis_depth: 10000,
514 validate_paths: true,
515 rate_limiting: RateLimitConfig {
516 enabled: true,
517 requests_per_minute: 500,
518 max_concurrent: 30,
519 burst_size: 100,
520 },
521 },
522 caching: CachingConfig {
523 enabled: true,
524 cache_dir: PathBuf::from("./cache/enterprise"),
525 max_cache_size_mb: 8192,
526 analysis_ttl: Duration::from_secs(14400),
527 content_ttl: Duration::from_secs(7200),
528 enable_compression: true,
529 cleanup_interval: Duration::from_secs(900),
530 },
531 },
532 );
533
534 info!(
535 "Registered {} built-in configuration profiles",
536 self.profiles.len()
537 );
538 }
539
540 pub fn list_profiles(&self) -> Vec<String> {
542 self.profiles.keys().cloned().collect()
543 }
544
545 pub fn get_profile(&self, name: &str) -> Option<&CodePrismProfile> {
547 self.profiles.get(name)
548 }
549
550 pub fn set_active_profile(&mut self, name: String) -> Result<()> {
552 if self.profiles.contains_key(&name) {
553 self.active_profile = Some(name.clone());
554 info!("Activated configuration profile: {}", name);
555 Ok(())
556 } else {
557 Err(crate::Error::server_init(format!(
558 "Profile '{name}' not found"
559 )))
560 }
561 }
562
563 pub fn get_active_profile(&self) -> Option<&CodePrismProfile> {
565 self.active_profile
566 .as_ref()
567 .and_then(|name| self.profiles.get(name))
568 }
569
570 pub fn register_profile(&mut self, profile: CodePrismProfile) {
572 let name = profile.name.clone();
573 self.profiles.insert(name.clone(), profile);
574 info!("Registered custom configuration profile: {}", name);
575 }
576
577 pub fn validate_profile(&self, profile: &CodePrismProfile) -> Result<Vec<String>> {
579 let mut warnings = Vec::new();
580
581 if profile.settings.memory_limit_mb < 512 {
583 warnings.push("Memory limit is very low, may cause performance issues".to_string());
584 }
585
586 if profile.settings.memory_limit_mb > 32768 {
587 warnings
588 .push("Memory limit is very high, ensure system has sufficient RAM".to_string());
589 }
590
591 if profile.settings.batch_size > 200 {
593 warnings.push("Batch size is very high, may cause memory pressure".to_string());
594 }
595
596 if profile.settings.max_file_size_mb > 100 {
598 warnings
599 .push("Max file size is very high, may cause long processing times".to_string());
600 }
601
602 if profile.settings.default_timeout.as_secs() > 600 {
604 warnings.push("Default timeout is very high, clients may disconnect".to_string());
605 }
606
607 if profile.caching.enabled && profile.caching.max_cache_size_mb > 10240 {
609 warnings.push("Cache size is very large, ensure sufficient disk space".to_string());
610 }
611
612 if !profile.security.validate_paths {
614 warnings.push("Path validation is disabled, security risk in production".to_string());
615 }
616
617 if profile.security.rate_limiting.enabled
618 && profile.security.rate_limiting.requests_per_minute > 1000
619 {
620 warnings.push("Rate limit is very high, may not prevent abuse effectively".to_string());
621 }
622
623 Ok(warnings)
624 }
625
626 pub fn profile_from_env() -> Result<CodePrismProfile> {
628 let profile_name =
629 std::env::var("CODEPRISM_PROFILE").unwrap_or_else(|_| "development".to_string());
630
631 let base_profile = if profile_name == "development"
633 || profile_name == "production"
634 || profile_name == "enterprise"
635 {
636 let manager = Self::new();
637 manager.get_profile(&profile_name).cloned()
638 } else {
639 None
640 };
641
642 let mut profile = base_profile.unwrap_or_else(|| {
643 let manager = Self::new();
644 manager.get_profile("development").unwrap().clone()
645 });
646
647 if let Ok(memory_limit) = std::env::var("CODEPRISM_MEMORY_LIMIT_MB") {
649 if let Ok(limit) = memory_limit.parse::<usize>() {
650 profile.settings.memory_limit_mb = limit;
651 }
652 }
653
654 if let Ok(batch_size) = std::env::var("CODEPRISM_BATCH_SIZE") {
655 if let Ok(size) = batch_size.parse::<usize>() {
656 profile.settings.batch_size = size;
657 }
658 }
659
660 if let Ok(timeout) = std::env::var("CODEPRISM_TIMEOUT_SECS") {
661 if let Ok(secs) = timeout.parse::<u64>() {
662 profile.settings.default_timeout = Duration::from_secs(secs);
663 }
664 }
665
666 if let Ok(enable_cache) = std::env::var("CODEPRISM_ENABLE_CACHE") {
667 profile.caching.enabled = enable_cache.to_lowercase() == "true";
668 }
669
670 if let Ok(cache_dir) = std::env::var("CODEPRISM_CACHE_DIR") {
671 profile.caching.cache_dir = PathBuf::from(cache_dir);
672 }
673
674 profile.name = format!("{profile_name}_env");
675 profile.description = format!("Environment-configured {profile_name} profile");
676
677 Ok(profile)
678 }
679}
680
681impl Config {
682 pub async fn from_env() -> Result<Self> {
684 let profile = ConfigProfileManager::profile_from_env()?;
685 let manager = ConfigProfileManager::new();
686
687 Ok(Self { profile, manager })
688 }
689
690 pub async fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
692 let path_ref = path.as_ref();
693 let extension = path_ref.extension().and_then(|s| s.to_str());
694 let content = tokio::fs::read_to_string(path_ref).await?;
695
696 let profile: CodePrismProfile = match extension {
697 Some("toml") => toml::from_str(&content)?,
698 Some("yaml") | Some("yml") => serde_yaml::from_str(&content)?,
699 Some("json") => serde_json::from_str(&content)?,
700 _ => {
701 toml::from_str(&content)
703 .or_else(|_| serde_yaml::from_str(&content))
704 .or_else(|_| serde_json::from_str(&content))?
705 }
706 };
707
708 let manager = ConfigProfileManager::new();
709 Ok(Self { profile, manager })
710 }
711
712 pub async fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
714 let content = match path.as_ref().extension().and_then(|s| s.to_str()) {
715 Some("toml") => toml::to_string_pretty(&self.profile)?,
716 Some("yaml") | Some("yml") => serde_yaml::to_string(&self.profile)?,
717 Some("json") => serde_json::to_string_pretty(&self.profile)?,
718 _ => toml::to_string_pretty(&self.profile)?, };
720 tokio::fs::write(path, content).await?;
721 Ok(())
722 }
723
724 pub fn validate(&self) -> Result<Vec<String>> {
726 if self.profile.settings.name.is_empty() {
727 return Err(crate::Error::server_init("Server name cannot be empty"));
728 }
729
730 if self.profile.settings.max_concurrent_operations == 0 {
731 return Err(crate::Error::server_init(
732 "Max concurrent operations must be greater than 0",
733 ));
734 }
735
736 if self.profile.settings.max_file_size_mb == 0 {
737 return Err(crate::Error::server_init(
738 "Max file size must be greater than 0",
739 ));
740 }
741
742 self.manager.validate_profile(&self.profile)
744 }
745
746 pub fn is_tool_enabled(&self, tool_name: &str) -> bool {
748 if self
750 .profile
751 .tools
752 .disabled_tools
753 .contains(&tool_name.to_string())
754 {
755 return false;
756 }
757
758 let tool_category = match tool_name {
760 "trace_path" | "find_dependencies" | "find_references" | "explain_symbol"
761 | "search_symbols" => Some(ToolCategory::CoreNavigation),
762 "search_content" | "find_patterns" | "semantic_search" | "search_by_type"
763 | "advanced_search" => Some(ToolCategory::SearchDiscovery),
764 "analyze_complexity"
765 | "analyze_control_flow"
766 | "analyze_code_quality"
767 | "analyze_performance" => Some(ToolCategory::Analysis),
768 "provide_guidance" | "optimize_code" | "batch_process" | "workflow_automation" => {
769 Some(ToolCategory::Workflow)
770 }
771 _ => None,
772 };
773
774 if let Some(category) = tool_category {
775 self.profile.tools.enabled_categories.contains(&category)
776 } else {
777 true }
779 }
780
781 pub fn get_tool_config(&self, tool_name: &str) -> Option<&ToolConfig> {
783 self.profile.tools.tool_configs.get(tool_name)
784 }
785
786 pub fn server(&self) -> ServerConfig {
788 ServerConfig {
789 name: self.profile.settings.name.clone(),
790 version: self.profile.settings.version.clone(),
791 max_concurrent_tools: self.profile.settings.max_concurrent_operations,
792 request_timeout_secs: self.profile.settings.default_timeout.as_secs(),
793 }
794 }
795
796 pub fn tools(&self) -> ToolsConfigCompat {
798 ToolsConfigCompat {
799 enable_core: self
800 .profile
801 .tools
802 .enabled_categories
803 .contains(&ToolCategory::CoreNavigation),
804 enable_search: self
805 .profile
806 .tools
807 .enabled_categories
808 .contains(&ToolCategory::SearchDiscovery),
809 enable_analysis: self
810 .profile
811 .tools
812 .enabled_categories
813 .contains(&ToolCategory::Analysis),
814 enable_workflow: self
815 .profile
816 .tools
817 .enabled_categories
818 .contains(&ToolCategory::Workflow),
819 }
820 }
821
822 pub fn analysis(&self) -> AnalysisConfigCompat {
824 AnalysisConfigCompat {
825 max_file_size_bytes: self.profile.settings.max_file_size_mb * 1024 * 1024,
826 max_files_per_request: self.profile.settings.batch_size,
827 enable_caching: self.profile.caching.enabled,
828 cache_ttl_secs: self.profile.caching.analysis_ttl.as_secs(),
829 }
830 }
831}
832
833#[derive(Debug, Clone)]
835pub struct ServerConfig {
836 pub name: String,
837 pub version: String,
838 pub max_concurrent_tools: usize,
839 pub request_timeout_secs: u64,
840}
841
842#[derive(Debug, Clone)]
843pub struct ToolsConfigCompat {
844 pub enable_core: bool,
845 pub enable_search: bool,
846 pub enable_analysis: bool,
847 pub enable_workflow: bool,
848}
849
850#[derive(Debug, Clone)]
851pub struct AnalysisConfigCompat {
852 pub max_file_size_bytes: usize,
853 pub max_files_per_request: usize,
854 pub enable_caching: bool,
855 pub cache_ttl_secs: u64,
856}
857
858impl Default for Config {
859 fn default() -> Self {
860 let manager = ConfigProfileManager::new();
861 let profile = manager.get_profile("development").unwrap().clone();
862 Self { profile, manager }
863 }
864}
865
866impl Default for ConfigProfileManager {
867 fn default() -> Self {
868 Self::new()
869 }
870}