1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::path::PathBuf;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct AgentConfig {
27 pub enabled: bool,
29 pub settings: HashMap<String, serde_json::Value>,
31}
32
33impl Default for AgentConfig {
34 fn default() -> Self {
35 Self {
36 enabled: true,
37 settings: HashMap::new(),
38 }
39 }
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct AgentInput {
72 pub task: AgentTask,
74 pub context: ProjectContext,
76 pub config: AgentConfig,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct AgentTask {
103 pub id: String,
105 pub task_type: TaskType,
107 pub target: TaskTarget,
109 pub options: TaskOptions,
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
118pub enum TaskType {
119 CodeReview,
121 TestGeneration,
123 Documentation,
125 Refactoring,
127 SecurityAnalysis,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct TaskTarget {
136 pub files: Vec<PathBuf>,
138 pub scope: TaskScope,
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
146pub enum TaskScope {
147 File,
149 Module,
151 Project,
153}
154
155#[derive(Debug, Clone, Default, Serialize, Deserialize)]
160pub struct TaskOptions {
161 pub custom: HashMap<String, serde_json::Value>,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct ProjectContext {
171 pub name: String,
173 pub root: PathBuf,
175}
176
177#[derive(Debug, Clone, Default, Serialize, Deserialize)]
202pub struct AgentOutput {
203 pub findings: Vec<Finding>,
205 pub suggestions: Vec<Suggestion>,
207 pub generated: Vec<GeneratedContent>,
209 pub metadata: AgentMetadata,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct Finding {
219 pub id: String,
221 pub severity: Severity,
223 pub category: String,
225 pub message: String,
227 pub location: Option<CodeLocation>,
229 pub suggestion: Option<String>,
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
237pub enum Severity {
238 Info,
240 Warning,
242 Critical,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct CodeLocation {
251 pub file: PathBuf,
253 pub line: usize,
255 pub column: usize,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
264pub struct Suggestion {
265 pub id: String,
267 pub description: String,
269 pub diff: Option<FileDiff>,
271 pub auto_fixable: bool,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct FileDiff {
280 pub file: PathBuf,
282 pub content: String,
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct GeneratedContent {
292 pub file: PathBuf,
294 pub content: String,
296}
297
298#[derive(Debug, Clone, Default, Serialize, Deserialize)]
303pub struct AgentMetadata {
304 pub agent_id: String,
306 pub execution_time_ms: u64,
308 pub tokens_used: usize,
310}
311
312#[derive(Debug, Clone, Default)]
316pub struct AgentMetrics {
317 pub execution_count: u64,
319 pub success_count: u64,
321 pub error_count: u64,
323 pub avg_duration_ms: f64,
325}
326
327#[derive(Debug, Clone, Default, Serialize, Deserialize)]
332pub struct ConfigSchema {
333 pub properties: HashMap<String, serde_json::Value>,
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340
341 #[test]
342 fn test_agent_config_default() {
343 let config = AgentConfig::default();
344 assert!(config.enabled);
345 assert!(config.settings.is_empty());
346 }
347
348 #[test]
349 fn test_agent_config_serialization() {
350 let config = AgentConfig {
351 enabled: true,
352 settings: {
353 let mut map = HashMap::new();
354 map.insert("key".to_string(), serde_json::json!("value"));
355 map
356 },
357 };
358
359 let json = serde_json::to_string(&config).expect("serialization failed");
360 let deserialized: AgentConfig =
361 serde_json::from_str(&json).expect("deserialization failed");
362
363 assert_eq!(deserialized.enabled, config.enabled);
364 assert_eq!(deserialized.settings, config.settings);
365 }
366
367 #[test]
368 fn test_agent_output_default() {
369 let output = AgentOutput::default();
370 assert!(output.findings.is_empty());
371 assert!(output.suggestions.is_empty());
372 assert!(output.generated.is_empty());
373 }
374
375 #[test]
376 fn test_agent_output_serialization() {
377 let output = AgentOutput {
378 findings: vec![Finding {
379 id: "finding-1".to_string(),
380 severity: Severity::Warning,
381 category: "quality".to_string(),
382 message: "Test finding".to_string(),
383 location: None,
384 suggestion: None,
385 }],
386 suggestions: vec![],
387 generated: vec![],
388 metadata: AgentMetadata::default(),
389 };
390
391 let json = serde_json::to_string(&output).expect("serialization failed");
392 let deserialized: AgentOutput =
393 serde_json::from_str(&json).expect("deserialization failed");
394
395 assert_eq!(deserialized.findings.len(), 1);
396 assert_eq!(deserialized.findings[0].id, "finding-1");
397 assert_eq!(deserialized.findings[0].severity, Severity::Warning);
398 }
399
400 #[test]
401 fn test_severity_ordering() {
402 assert!(Severity::Info < Severity::Warning);
403 assert!(Severity::Warning < Severity::Critical);
404 assert!(Severity::Info < Severity::Critical);
405 }
406
407 #[test]
408 fn test_task_type_serialization() {
409 let task_type = TaskType::CodeReview;
410 let json = serde_json::to_string(&task_type).expect("serialization failed");
411 let deserialized: TaskType = serde_json::from_str(&json).expect("deserialization failed");
412
413 assert_eq!(deserialized, task_type);
414 }
415
416 #[test]
417 fn test_task_scope_serialization() {
418 let scope = TaskScope::Module;
419 let json = serde_json::to_string(&scope).expect("serialization failed");
420 let deserialized: TaskScope = serde_json::from_str(&json).expect("deserialization failed");
421
422 assert_eq!(deserialized, scope);
423 }
424
425 #[test]
426 fn test_finding_with_location() {
427 let finding = Finding {
428 id: "finding-1".to_string(),
429 severity: Severity::Critical,
430 category: "security".to_string(),
431 message: "Security vulnerability".to_string(),
432 location: Some(CodeLocation {
433 file: PathBuf::from("src/main.rs"),
434 line: 42,
435 column: 10,
436 }),
437 suggestion: Some("Use safe alternative".to_string()),
438 };
439
440 assert_eq!(finding.id, "finding-1");
441 assert_eq!(finding.severity, Severity::Critical);
442 assert!(finding.location.is_some());
443 assert!(finding.suggestion.is_some());
444
445 let location = finding.location.unwrap();
446 assert_eq!(location.line, 42);
447 assert_eq!(location.column, 10);
448 }
449
450 #[test]
451 fn test_suggestion_auto_fixable() {
452 let suggestion = Suggestion {
453 id: "suggestion-1".to_string(),
454 description: "Fix naming".to_string(),
455 diff: None,
456 auto_fixable: true,
457 };
458
459 assert!(suggestion.auto_fixable);
460 }
461
462 #[test]
463 fn test_agent_metadata_serialization() {
464 let metadata = AgentMetadata {
465 agent_id: "test-agent".to_string(),
466 execution_time_ms: 1000,
467 tokens_used: 500,
468 };
469
470 let json = serde_json::to_string(&metadata).expect("serialization failed");
471 let deserialized: AgentMetadata =
472 serde_json::from_str(&json).expect("deserialization failed");
473
474 assert_eq!(deserialized.agent_id, "test-agent");
475 assert_eq!(deserialized.execution_time_ms, 1000);
476 assert_eq!(deserialized.tokens_used, 500);
477 }
478
479 #[test]
480 fn test_agent_metrics_default() {
481 let metrics = AgentMetrics::default();
482 assert_eq!(metrics.execution_count, 0);
483 assert_eq!(metrics.success_count, 0);
484 assert_eq!(metrics.error_count, 0);
485 assert_eq!(metrics.avg_duration_ms, 0.0);
486 }
487
488 #[test]
489 fn test_config_schema_default() {
490 let schema = ConfigSchema::default();
491 assert!(schema.properties.is_empty());
492 }
493
494 #[test]
495 fn test_task_target_multiple_files() {
496 let target = TaskTarget {
497 files: vec![
498 PathBuf::from("src/main.rs"),
499 PathBuf::from("src/lib.rs"),
500 PathBuf::from("tests/test.rs"),
501 ],
502 scope: TaskScope::Project,
503 };
504
505 assert_eq!(target.files.len(), 3);
506 assert_eq!(target.scope, TaskScope::Project);
507 }
508
509 #[test]
510 fn test_project_context() {
511 let context = ProjectContext {
512 name: "my-project".to_string(),
513 root: PathBuf::from("/home/user/projects/my-project"),
514 };
515
516 assert_eq!(context.name, "my-project");
517 assert_eq!(
518 context.root,
519 PathBuf::from("/home/user/projects/my-project")
520 );
521 }
522
523 #[test]
524 fn test_generated_content() {
525 let content = GeneratedContent {
526 file: PathBuf::from("generated/test.rs"),
527 content: "// Generated code".to_string(),
528 };
529
530 assert_eq!(content.file, PathBuf::from("generated/test.rs"));
531 assert_eq!(content.content, "// Generated code");
532 }
533}