1use std::collections::HashMap;
23use std::path::{Path, PathBuf};
24use serde::{Deserialize, Serialize};
25use chrono::{DateTime, Utc};
26use uuid::Uuid;
27
28#[derive(Debug, thiserror::Error)]
30pub enum DocsError {
31 #[error("IO error: {0}")]
32 Io(#[from] std::io::Error),
33
34 #[error("JSON error: {0}")]
35 Json(#[from] serde_json::Error),
36
37 #[error("YAML error: {0}")]
38 Yaml(#[from] serde_yaml::Error),
39
40 #[error("TOML error: {0}")]
41 Toml(#[from] toml::de::Error),
42
43 #[error("Regex error: {0}")]
44 Regex(#[from] regex::Error),
45
46 #[error("Template error: {0}")]
47 Template(String),
48
49 #[error("Parse error: {0}")]
50 Parse(String),
51
52 #[error("Search error: {0}")]
53 Search(String),
54
55 #[error("Config error: {0}")]
56 Config(String),
57
58 #[error("Server error: {0}")]
59 Server(String),
60
61 #[error("Anyhow error: {0}")]
62 Anyhow(#[from] anyhow::Error),
63}
64
65pub type Result<T> = std::result::Result<T, DocsError>;
66
67#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
69pub enum DocType {
70 Module,
72 Function,
74 Struct,
76 Enum,
78 Trait,
80 Constant,
82 Macro,
84 TypeAlias,
86 Method,
88 Field,
90 Variant,
92 AssociatedType,
94 AssociatedConstant,
96}
97
98impl std::fmt::Display for DocType {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 let name = match self {
101 DocType::Module => "Module",
102 DocType::Function => "Function",
103 DocType::Struct => "Struct",
104 DocType::Enum => "Enum",
105 DocType::Trait => "Trait",
106 DocType::Constant => "Constant",
107 DocType::Macro => "Macro",
108 DocType::TypeAlias => "TypeAlias",
109 DocType::Method => "Method",
110 DocType::Field => "Field",
111 DocType::Variant => "Variant",
112 DocType::AssociatedType => "AssociatedType",
113 DocType::AssociatedConstant => "AssociatedConstant",
114 };
115 write!(f, "{}", name)
116 }
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct DocItem {
122 pub id: String,
124
125 pub name: String,
127
128 pub doc_type: DocType,
130
131 pub content: String,
133
134 pub signature: Option<String>,
136
137 pub file_path: PathBuf,
139
140 pub line_number: Option<usize>,
142
143 pub parent_id: Option<String>,
145
146 pub children: Vec<String>,
148
149 pub created_at: DateTime<Utc>,
151
152 pub updated_at: DateTime<Utc>,
154
155 pub metadata: HashMap<String, serde_json::Value>,
157
158 pub related_items: Vec<String>,
160
161 pub tags: Vec<String>,
163}
164
165impl DocItem {
166 pub fn new(name: String, doc_type: DocType, content: String) -> Self {
168 let now = Utc::now();
169 Self {
170 id: Uuid::new_v4().to_string(),
171 name,
172 doc_type,
173 content,
174 signature: None,
175 file_path: PathBuf::new(),
176 line_number: None,
177 parent_id: None,
178 children: vec![],
179 created_at: now,
180 updated_at: now,
181 metadata: HashMap::new(),
182 related_items: vec![],
183 tags: vec![],
184 }
185 }
186
187 pub fn with_file_path(mut self, path: PathBuf) -> Self {
189 self.file_path = path;
190 self
191 }
192
193 pub fn with_line_number(mut self, line: usize) -> Self {
195 self.line_number = Some(line);
196 self
197 }
198
199 pub fn with_signature(mut self, signature: String) -> Self {
201 self.signature = Some(signature);
202 self
203 }
204
205 pub fn with_parent(mut self, parent_id: String) -> Self {
207 self.parent_id = Some(parent_id);
208 self
209 }
210
211 pub fn with_tag(mut self, tag: String) -> Self {
213 self.tags.push(tag);
214 self
215 }
216
217 pub fn with_metadata(mut self, key: String, value: serde_json::Value) -> Self {
219 self.metadata.insert(key, value);
220 self
221 }
222
223 pub fn html_id(&self) -> String {
225 format!("{}-{}", self.doc_type.to_string().to_lowercase(), self.id)
226 }
227
228 pub fn slug(&self) -> String {
230 self.name
231 .to_lowercase()
232 .replace(" ", "-")
233 .replace("_", "-")
234 .chars()
235 .filter(|c| c.is_alphanumeric() || *c == '-')
236 .collect::<String>()
237 }
238}
239
240impl Default for DocItem {
241 fn default() -> Self {
242 Self::new(String::new(), DocType::Module, String::new())
243 }
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct DocsConfig {
249 pub name: String,
251
252 pub version: String,
254
255 pub description: Option<String>,
257
258 pub authors: Vec<String>,
260
261 pub repository: Option<String>,
263
264 pub homepage: Option<String>,
266
267 pub license: Option<String>,
269
270 pub input_dir: PathBuf,
272
273 pub output_dir: PathBuf,
275
276 pub template_dir: Option<PathBuf>,
278
279 pub theme: String,
281
282 pub formats: Vec<OutputFormat>,
284
285 pub exclude_patterns: Vec<String>,
287
288 pub include_extensions: Vec<String>,
290
291 pub server: ServerConfig,
293
294 pub search: SearchConfig,
296
297 pub extra: HashMap<String, serde_json::Value>,
299}
300
301impl Default for DocsConfig {
302 fn default() -> Self {
303 Self {
304 name: "My Project".to_string(),
305 version: "0.1.0".to_string(),
306 description: None,
307 authors: vec![],
308 repository: None,
309 homepage: None,
310 license: None,
311 input_dir: PathBuf::from("src"),
312 output_dir: PathBuf::from("docs"),
313 template_dir: None,
314 theme: "default".to_string(),
315 formats: vec![OutputFormat::Html],
316 exclude_patterns: vec![
317 "target".to_string(),
318 "node_modules".to_string(),
319 ".git".to_string(),
320 "*.tmp".to_string(),
321 ],
322 include_extensions: vec![
323 "rs".to_string(),
324 "js".to_string(),
325 "ts".to_string(),
326 "py".to_string(),
327 "go".to_string(),
328 "md".to_string(),
329 ],
330 server: ServerConfig::default(),
331 search: SearchConfig::default(),
332 extra: HashMap::new(),
333 }
334 }
335}
336
337#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
339pub enum OutputFormat {
340 Html,
342 Markdown,
344 Json,
346 Pdf,
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize)]
352pub struct ServerConfig {
353 pub host: String,
355
356 pub port: u16,
358
359 pub https: bool,
361
362 pub open: bool,
364
365 pub reload: bool,
367}
368
369impl Default for ServerConfig {
370 fn default() -> Self {
371 Self {
372 host: "localhost".to_string(),
373 port: 3000,
374 https: false,
375 open: false,
376 reload: true,
377 }
378 }
379}
380
381#[derive(Debug, Clone, Serialize, Deserialize)]
383pub struct SearchConfig {
384 pub enabled: bool,
386
387 pub index_file: String,
389
390 pub max_results: usize,
392
393 pub fuzzy: bool,
395}
396
397impl Default for SearchConfig {
398 fn default() -> Self {
399 Self {
400 enabled: true,
401 index_file: "search-index.json".to_string(),
402 max_results: 50,
403 fuzzy: true,
404 }
405 }
406}
407
408#[derive(Debug, Clone)]
410pub struct GenerateResult {
411 pub documents_generated: usize,
413
414 pub files_processed: usize,
416
417 pub output_dir: PathBuf,
419
420 pub generation_time: std::time::Duration,
422
423 pub timestamp: DateTime<Utc>,
425
426 pub errors: usize,
428}
429
430impl GenerateResult {
431 pub fn new() -> Self {
432 Self {
433 documents_generated: 0,
434 files_processed: 0,
435 output_dir: PathBuf::new(),
436 generation_time: std::time::Duration::default(),
437 timestamp: Utc::now(),
438 errors: 0,
439 }
440 }
441
442 pub fn success(
443 mut self,
444 docs: usize,
445 files: usize,
446 output_dir: PathBuf,
447 duration: std::time::Duration,
448 ) -> Self {
449 self.documents_generated = docs;
450 self.files_processed = files;
451 self.output_dir = output_dir;
452 self.generation_time = duration;
453 self
454 }
455
456 pub fn with_errors(mut self, errors: usize) -> Self {
457 self.errors = errors;
458 self
459 }
460}
461
462pub mod parser;
464pub mod generator;
465pub mod server;
466pub mod search;
467pub mod template;
468pub mod config;