ricecoder_storage/industry/
windsurf.rs

1//! Windsurf IDE configuration adapter
2//!
3//! Reads and converts Windsurf IDE configuration files (.windsurfrules)
4//! into RiceCoder's internal configuration format.
5
6use crate::config::{Config, SteeringRule};
7use crate::error::StorageResult;
8use crate::types::DocumentFormat;
9use std::path::Path;
10use tracing::debug;
11
12use super::adapter::IndustryFileAdapter;
13
14/// Windsurf IDE adapter
15pub struct WindsurfAdapter;
16
17impl WindsurfAdapter {
18    /// Create a new Windsurf adapter
19    pub fn new() -> Self {
20        WindsurfAdapter
21    }
22
23    /// Read .windsurfrules file
24    fn read_windsurfrules(&self, project_root: &Path) -> StorageResult<Option<String>> {
25        let windsurfrules_path = project_root.join(".windsurfrules");
26
27        if !windsurfrules_path.exists() {
28            debug!("No .windsurfrules file found at {:?}", windsurfrules_path);
29            return Ok(None);
30        }
31
32        debug!("Reading .windsurfrules from {:?}", windsurfrules_path);
33        let content = std::fs::read_to_string(&windsurfrules_path).map_err(|e| {
34            crate::error::StorageError::io_error(
35                windsurfrules_path.clone(),
36                crate::error::IoOperation::Read,
37                e,
38            )
39        })?;
40
41        Ok(Some(content))
42    }
43}
44
45impl Default for WindsurfAdapter {
46    fn default() -> Self {
47        Self::new()
48    }
49}
50
51impl IndustryFileAdapter for WindsurfAdapter {
52    fn name(&self) -> &'static str {
53        "windsurf"
54    }
55
56    fn can_handle(&self, project_root: &Path) -> bool {
57        project_root.join(".windsurfrules").exists()
58    }
59
60    fn read_config(&self, project_root: &Path) -> StorageResult<Config> {
61        let mut config = Config::default();
62
63        if let Ok(Some(rules_content)) = self.read_windsurfrules(project_root) {
64            debug!("Adding Windsurf rules as steering rule");
65            config.steering.push(SteeringRule {
66                name: "windsurf-rules".to_string(),
67                content: rules_content,
68                format: DocumentFormat::Markdown,
69            });
70        }
71
72        Ok(config)
73    }
74
75    fn priority(&self) -> u32 {
76        // Windsurf has medium priority
77        50
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use std::fs;
85    use tempfile::TempDir;
86
87    #[test]
88    fn test_windsurf_adapter_detects_windsurfrules() {
89        let temp_dir = TempDir::new().unwrap();
90        let windsurfrules_path = temp_dir.path().join(".windsurfrules");
91        fs::write(&windsurfrules_path, "# Windsurf rules").unwrap();
92
93        let adapter = WindsurfAdapter::new();
94        assert!(adapter.can_handle(temp_dir.path()));
95    }
96
97    #[test]
98    fn test_windsurf_adapter_no_file() {
99        let temp_dir = TempDir::new().unwrap();
100
101        let adapter = WindsurfAdapter::new();
102        assert!(!adapter.can_handle(temp_dir.path()));
103    }
104
105    #[test]
106    fn test_windsurf_adapter_reads_windsurfrules() {
107        let temp_dir = TempDir::new().unwrap();
108        let windsurfrules_path = temp_dir.path().join(".windsurfrules");
109        let rules = "# Windsurf Rules\nBe productive";
110        fs::write(&windsurfrules_path, rules).unwrap();
111
112        let adapter = WindsurfAdapter::new();
113        let config = adapter.read_config(temp_dir.path()).unwrap();
114
115        assert_eq!(config.steering.len(), 1);
116        assert_eq!(config.steering[0].name, "windsurf-rules");
117        assert_eq!(config.steering[0].content, rules);
118        assert_eq!(config.steering[0].format, DocumentFormat::Markdown);
119    }
120
121    #[test]
122    fn test_windsurf_adapter_priority() {
123        let adapter = WindsurfAdapter::new();
124        assert_eq!(adapter.priority(), 50);
125    }
126
127    #[test]
128    fn test_windsurf_adapter_name() {
129        let adapter = WindsurfAdapter::new();
130        assert_eq!(adapter.name(), "windsurf");
131    }
132}