driven/parser/
windsurf.rs1use super::{RuleParser, UnifiedRule, extract_bullet_points, parse_markdown_sections};
4use crate::{DrivenError, Editor, Result};
5use std::path::Path;
6
7#[derive(Debug, Default)]
9pub struct WindsurfParser;
10
11impl WindsurfParser {
12 pub fn new() -> Self {
14 Self
15 }
16}
17
18impl RuleParser for WindsurfParser {
19 fn parse_file(&self, path: &Path) -> Result<Vec<UnifiedRule>> {
20 let content = std::fs::read_to_string(path)
21 .map_err(|e| DrivenError::Parse(format!("Failed to read {}: {}", path.display(), e)))?;
22 self.parse_content(&content)
23 }
24
25 fn parse_content(&self, content: &str) -> Result<Vec<UnifiedRule>> {
26 let mut rules = Vec::new();
27 let sections = parse_markdown_sections(content);
28
29 if sections.is_empty() {
30 if !content.trim().is_empty() {
32 rules.push(UnifiedRule::raw(content.trim()));
33 }
34 return Ok(rules);
35 }
36
37 for (heading, body) in sections {
39 let lower = heading.to_lowercase();
40
41 if lower.contains("rule") || lower.contains("guideline") || lower.contains("standard") {
42 let points = extract_bullet_points(&body);
43 for (i, point) in points.into_iter().enumerate() {
44 rules.push(UnifiedRule::Standard {
45 category: crate::format::RuleCategory::Style,
46 priority: i as u8,
47 description: point,
48 pattern: None,
49 });
50 }
51 } else if lower.contains("context") || lower.contains("project") {
52 let points = extract_bullet_points(&body);
53 rules.push(UnifiedRule::Context {
54 includes: Vec::new(),
55 excludes: Vec::new(),
56 focus: points,
57 });
58 } else {
59 if !body.trim().is_empty() {
61 rules.push(UnifiedRule::raw(format!("# {}\n{}", heading, body)));
62 }
63 }
64 }
65
66 Ok(rules)
67 }
68
69 fn editor(&self) -> Editor {
70 Editor::Windsurf
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn test_parse_windsurfrules() {
80 let content = r#"# Coding Rules
81
82- Always use TypeScript strict mode
83- Prefer functional components
84- Use proper error boundaries
85
86# Project Context
87
88- This is a Next.js application
89- Using Tailwind CSS for styling
90"#;
91
92 let parser = WindsurfParser::new();
93 let rules = parser.parse_content(content).unwrap();
94
95 assert!(!rules.is_empty());
96 }
97}