ricecoder_storage/industry/
continue_dev.rs1use 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
14pub struct ContinueDevAdapter;
16
17impl ContinueDevAdapter {
18 pub fn new() -> Self {
20 ContinueDevAdapter
21 }
22
23 fn read_continue_config(&self, project_root: &Path) -> StorageResult<Option<String>> {
25 let continue_dir = project_root.join(".continue");
26
27 if !continue_dir.exists() {
28 debug!("No .continue directory found at {:?}", continue_dir);
29 return Ok(None);
30 }
31
32 debug!("Reading .continue configuration from {:?}", continue_dir);
33
34 let config_path = continue_dir.join("config.json");
36 if config_path.exists() {
37 let content = std::fs::read_to_string(&config_path).map_err(|e| {
38 crate::error::StorageError::io_error(
39 config_path.clone(),
40 crate::error::IoOperation::Read,
41 e,
42 )
43 })?;
44 return Ok(Some(content));
45 }
46
47 let mut combined_content = String::new();
49 if let Ok(entries) = std::fs::read_dir(&continue_dir) {
50 for entry in entries.flatten() {
51 if let Ok(metadata) = entry.metadata() {
52 if metadata.is_file() {
53 if let Ok(content) = std::fs::read_to_string(entry.path()) {
54 combined_content.push_str(&format!(
55 "# {}\n{}\n\n",
56 entry.path().display(),
57 content
58 ));
59 }
60 }
61 }
62 }
63 }
64
65 if combined_content.is_empty() {
66 Ok(None)
67 } else {
68 Ok(Some(combined_content))
69 }
70 }
71}
72
73impl Default for ContinueDevAdapter {
74 fn default() -> Self {
75 Self::new()
76 }
77}
78
79impl IndustryFileAdapter for ContinueDevAdapter {
80 fn name(&self) -> &'static str {
81 "continue"
82 }
83
84 fn can_handle(&self, project_root: &Path) -> bool {
85 project_root.join(".continue").exists()
86 }
87
88 fn read_config(&self, project_root: &Path) -> StorageResult<Config> {
89 let mut config = Config::default();
90
91 if let Ok(Some(continue_config)) = self.read_continue_config(project_root) {
92 debug!("Adding Continue.dev configuration as steering rule");
93 config.steering.push(SteeringRule {
94 name: "continue-config".to_string(),
95 content: continue_config,
96 format: DocumentFormat::Markdown,
97 });
98 }
99
100 Ok(config)
101 }
102
103 fn priority(&self) -> u32 {
104 50
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use std::fs;
113 use tempfile::TempDir;
114
115 #[test]
116 fn test_continue_adapter_detects_directory() {
117 let temp_dir = TempDir::new().unwrap();
118 let continue_dir = temp_dir.path().join(".continue");
119 fs::create_dir(&continue_dir).unwrap();
120
121 let adapter = ContinueDevAdapter::new();
122 assert!(adapter.can_handle(temp_dir.path()));
123 }
124
125 #[test]
126 fn test_continue_adapter_no_directory() {
127 let temp_dir = TempDir::new().unwrap();
128
129 let adapter = ContinueDevAdapter::new();
130 assert!(!adapter.can_handle(temp_dir.path()));
131 }
132
133 #[test]
134 fn test_continue_adapter_reads_config_json() {
135 let temp_dir = TempDir::new().unwrap();
136 let continue_dir = temp_dir.path().join(".continue");
137 fs::create_dir(&continue_dir).unwrap();
138 let config_path = continue_dir.join("config.json");
139 let config_content = r#"{"models": ["gpt-4"]}"#;
140 fs::write(&config_path, config_content).unwrap();
141
142 let adapter = ContinueDevAdapter::new();
143 let config = adapter.read_config(temp_dir.path()).unwrap();
144
145 assert_eq!(config.steering.len(), 1);
146 assert_eq!(config.steering[0].name, "continue-config");
147 assert_eq!(config.steering[0].content, config_content);
148 }
149
150 #[test]
151 fn test_continue_adapter_reads_multiple_files() {
152 let temp_dir = TempDir::new().unwrap();
153 let continue_dir = temp_dir.path().join(".continue");
154 fs::create_dir(&continue_dir).unwrap();
155 fs::write(continue_dir.join("file1.txt"), "content1").unwrap();
156 fs::write(continue_dir.join("file2.txt"), "content2").unwrap();
157
158 let adapter = ContinueDevAdapter::new();
159 let config = adapter.read_config(temp_dir.path()).unwrap();
160
161 assert_eq!(config.steering.len(), 1);
162 assert_eq!(config.steering[0].name, "continue-config");
163 assert!(config.steering[0].content.contains("content1"));
164 assert!(config.steering[0].content.contains("content2"));
165 }
166
167 #[test]
168 fn test_continue_adapter_priority() {
169 let adapter = ContinueDevAdapter::new();
170 assert_eq!(adapter.priority(), 50);
171 }
172
173 #[test]
174 fn test_continue_adapter_name() {
175 let adapter = ContinueDevAdapter::new();
176 assert_eq!(adapter.name(), "continue");
177 }
178}