1use anyhow::{Result, anyhow};
7use async_trait::async_trait;
8use rust_embed::RustEmbed;
9
10use super::AssetManager;
11
12#[derive(RustEmbed)]
13#[folder = "templates/"]
14#[prefix = "templates/"]
15struct Templates;
16
17#[derive(RustEmbed)]
18#[folder = "dockerfiles/"]
19#[prefix = "dockerfiles/"]
20struct Dockerfiles;
21
22pub struct EmbeddedAssetManager;
24
25impl EmbeddedAssetManager {
26 pub fn new() -> Self {
28 Self
29 }
30}
31
32impl Default for EmbeddedAssetManager {
33 fn default() -> Self {
34 Self::new()
35 }
36}
37
38#[async_trait]
39impl AssetManager for EmbeddedAssetManager {
40 fn get_template(&self, template_type: &str) -> Result<String> {
41 let filename = format!("templates/{template_type}.md");
42
43 Templates::get(&filename)
44 .ok_or_else(|| anyhow!("Template '{template_type}' not found"))
45 .and_then(|file| {
46 String::from_utf8(file.data.to_vec())
47 .map_err(|e| anyhow!("Failed to decode template '{template_type}': {e}"))
48 })
49 }
50
51 fn get_dockerfile(&self, dockerfile_name: &str) -> Result<Vec<u8>> {
52 let path = format!("dockerfiles/{dockerfile_name}/Dockerfile");
53
54 Dockerfiles::get(&path)
55 .ok_or_else(|| anyhow!("Dockerfile '{dockerfile_name}' not found"))
56 .map(|file| file.data.to_vec())
57 }
58
59 fn get_dockerfile_file(&self, dockerfile_name: &str, file_path: &str) -> Result<Vec<u8>> {
60 let path = format!("dockerfiles/{dockerfile_name}/{file_path}");
61
62 Dockerfiles::get(&path)
63 .ok_or_else(|| {
64 anyhow!("File '{file_path}' not found in dockerfile '{dockerfile_name}'")
65 })
66 .map(|file| file.data.to_vec())
67 }
68
69 fn list_templates(&self) -> Vec<String> {
70 Templates::iter()
71 .filter_map(|path| {
72 if path.starts_with("templates/") && path.ends_with(".md") {
73 path.strip_prefix("templates/")
74 .and_then(|p| p.strip_suffix(".md"))
75 .map(|s| s.to_string())
76 } else {
77 None
78 }
79 })
80 .collect()
81 }
82
83 fn list_dockerfiles(&self) -> Vec<String> {
84 use std::collections::HashSet;
85
86 let mut dockerfiles = HashSet::new();
87
88 for path in Dockerfiles::iter() {
89 if path.starts_with("dockerfiles/") {
90 if let Some(remaining) = path.strip_prefix("dockerfiles/") {
91 if let Some(dockerfile_name) = remaining.split('/').next() {
92 dockerfiles.insert(dockerfile_name.to_string());
93 }
94 }
95 }
96 }
97
98 let mut result: Vec<String> = dockerfiles.into_iter().collect();
99 result.sort();
100 result
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_embedded_asset_manager_creation() {
110 let _manager = EmbeddedAssetManager::new();
111 }
112
113 #[test]
114 fn test_get_template_success() {
115 let manager = EmbeddedAssetManager::new();
116
117 let result = manager.get_template("feat");
119 assert!(result.is_ok());
120 let content = result.unwrap();
121 assert!(content.contains("Feature"));
122 assert!(content.contains("{{DESCRIPTION}}"));
123 }
124
125 #[test]
126 fn test_get_template_not_found() {
127 let manager = EmbeddedAssetManager::new();
128
129 let result = manager.get_template("nonexistent");
131 assert!(result.is_err());
132 assert!(result.unwrap_err().to_string().contains("not found"));
133 }
134
135 #[test]
136 fn test_list_templates() {
137 let manager = EmbeddedAssetManager::new();
138
139 let templates = manager.list_templates();
140 assert!(!templates.is_empty());
141
142 assert!(templates.contains(&"feat".to_string()));
144 assert!(templates.contains(&"fix".to_string()));
145 assert!(templates.contains(&"doc".to_string()));
146 assert!(templates.contains(&"plan".to_string()));
147 assert!(templates.contains(&"refactor".to_string()));
148 }
149
150 #[test]
151 fn test_get_dockerfile_success() {
152 let manager = EmbeddedAssetManager::new();
153
154 let result = manager.get_dockerfile("tsk-base");
156 assert!(result.is_ok());
157 let content = result.unwrap();
158 assert!(!content.is_empty());
159
160 let content_str = String::from_utf8_lossy(&content);
162 assert!(content_str.contains("FROM"));
163 }
164
165 #[test]
166 fn test_get_dockerfile_not_found() {
167 let manager = EmbeddedAssetManager::new();
168
169 let result = manager.get_dockerfile("nonexistent");
171 assert!(result.is_err());
172 assert!(result.unwrap_err().to_string().contains("not found"));
173 }
174
175 #[test]
176 fn test_list_dockerfiles() {
177 let manager = EmbeddedAssetManager::new();
178
179 let dockerfiles = manager.list_dockerfiles();
180 assert!(!dockerfiles.is_empty());
181
182 assert!(dockerfiles.contains(&"tsk-base".to_string()));
184 assert!(dockerfiles.contains(&"tsk-proxy".to_string()));
185 }
186
187 #[test]
188 fn test_get_dockerfile_file_success() {
189 let manager = EmbeddedAssetManager::new();
190
191 let result = manager.get_dockerfile_file("tsk-proxy", "squid.conf");
193 assert!(result.is_ok());
194 let content = result.unwrap();
195 assert!(!content.is_empty());
196
197 let content_str = String::from_utf8_lossy(&content);
199 assert!(content_str.contains("http_access"));
200 }
201
202 #[test]
203 fn test_get_dockerfile_file_not_found() {
204 let manager = EmbeddedAssetManager::new();
205
206 let result = manager.get_dockerfile_file("tsk-base", "nonexistent.txt");
208 assert!(result.is_err());
209 assert!(result.unwrap_err().to_string().contains("not found"));
210 }
211}