ricecoder_storage/industry/
copilot.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 CopilotAdapter;
16
17impl CopilotAdapter {
18 pub fn new() -> Self {
20 CopilotAdapter
21 }
22
23 fn read_copilot_instructions(&self, project_root: &Path) -> StorageResult<Option<String>> {
25 let copilot_path = project_root.join(".github/copilot-instructions.md");
26
27 if !copilot_path.exists() {
28 debug!(
29 "No .github/copilot-instructions.md file found at {:?}",
30 copilot_path
31 );
32 return Ok(None);
33 }
34
35 debug!(
36 "Reading .github/copilot-instructions.md from {:?}",
37 copilot_path
38 );
39 let content = std::fs::read_to_string(&copilot_path).map_err(|e| {
40 crate::error::StorageError::io_error(
41 copilot_path.clone(),
42 crate::error::IoOperation::Read,
43 e,
44 )
45 })?;
46
47 Ok(Some(content))
48 }
49}
50
51impl Default for CopilotAdapter {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57impl IndustryFileAdapter for CopilotAdapter {
58 fn name(&self) -> &'static str {
59 "copilot"
60 }
61
62 fn can_handle(&self, project_root: &Path) -> bool {
63 project_root
64 .join(".github/copilot-instructions.md")
65 .exists()
66 }
67
68 fn read_config(&self, project_root: &Path) -> StorageResult<Config> {
69 let mut config = Config::default();
70
71 if let Ok(Some(instructions)) = self.read_copilot_instructions(project_root) {
72 debug!("Adding Copilot instructions as steering rule");
73 config.steering.push(SteeringRule {
74 name: "copilot-instructions".to_string(),
75 content: instructions,
76 format: DocumentFormat::Markdown,
77 });
78 }
79
80 Ok(config)
81 }
82
83 fn priority(&self) -> u32 {
84 50
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use std::fs;
93 use tempfile::TempDir;
94
95 #[test]
96 fn test_copilot_adapter_detects_instructions() {
97 let temp_dir = TempDir::new().unwrap();
98 let github_dir = temp_dir.path().join(".github");
99 fs::create_dir(&github_dir).unwrap();
100 let copilot_path = github_dir.join("copilot-instructions.md");
101 fs::write(&copilot_path, "# Copilot Instructions").unwrap();
102
103 let adapter = CopilotAdapter::new();
104 assert!(adapter.can_handle(temp_dir.path()));
105 }
106
107 #[test]
108 fn test_copilot_adapter_no_file() {
109 let temp_dir = TempDir::new().unwrap();
110
111 let adapter = CopilotAdapter::new();
112 assert!(!adapter.can_handle(temp_dir.path()));
113 }
114
115 #[test]
116 fn test_copilot_adapter_reads_instructions() {
117 let temp_dir = TempDir::new().unwrap();
118 let github_dir = temp_dir.path().join(".github");
119 fs::create_dir(&github_dir).unwrap();
120 let copilot_path = github_dir.join("copilot-instructions.md");
121 let instructions = "# Copilot Instructions\nBe helpful";
122 fs::write(&copilot_path, instructions).unwrap();
123
124 let adapter = CopilotAdapter::new();
125 let config = adapter.read_config(temp_dir.path()).unwrap();
126
127 assert_eq!(config.steering.len(), 1);
128 assert_eq!(config.steering[0].name, "copilot-instructions");
129 assert_eq!(config.steering[0].content, instructions);
130 assert_eq!(config.steering[0].format, DocumentFormat::Markdown);
131 }
132
133 #[test]
134 fn test_copilot_adapter_priority() {
135 let adapter = CopilotAdapter::new();
136 assert_eq!(adapter.priority(), 50);
137 }
138
139 #[test]
140 fn test_copilot_adapter_name() {
141 let adapter = CopilotAdapter::new();
142 assert_eq!(adapter.name(), "copilot");
143 }
144}