#![cfg_attr(coverage_nightly, coverage(off))]
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PdmtTodo {
pub id: String,
pub content: String,
pub status: TodoStatus,
pub priority: TodoPriority,
pub estimated_hours: f32,
pub dependencies: Vec<String>,
pub quality_gates: TodoQualityGates,
pub validation_commands: ValidationCommands,
pub success_criteria: Vec<String>,
pub implementation_specs: ImplementationSpecs,
}
impl PdmtTodo {
#[must_use]
pub fn new(content: String, priority: TodoPriority) -> Self {
Self {
id: Uuid::new_v4().to_string(),
content,
status: TodoStatus::Pending,
priority,
estimated_hours: 4.0,
dependencies: Vec::new(),
quality_gates: TodoQualityGates::default(),
validation_commands: ValidationCommands::default(),
success_criteria: vec![
"Unit tests pass with >80% coverage".to_string(),
"All doctests execute successfully".to_string(),
"Quality proxy approves all changes".to_string(),
"Zero SATD comments present".to_string(),
],
implementation_specs: ImplementationSpecs::default(),
}
}
#[must_use]
pub fn get_primary_file_path(&self) -> String {
self.implementation_specs
.primary_files
.first()
.cloned()
.unwrap_or_else(|| "src/lib.rs".to_string())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TodoStatus {
Pending,
InProgress,
Completed,
Blocked,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TodoPriority {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TodoQualityGates {
pub coverage_requirement: f32,
pub doctest_requirement: bool,
pub property_test_requirement: bool,
pub example_requirement: bool,
pub complexity_limit: u32,
pub satd_tolerance: bool,
}
impl Default for TodoQualityGates {
fn default() -> Self {
Self {
coverage_requirement: 80.0,
doctest_requirement: true,
property_test_requirement: true,
example_requirement: true,
complexity_limit: 8,
satd_tolerance: false,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ValidationCommands {
pub unit_tests: String,
pub doctests: String,
pub property_tests: String,
pub examples: Vec<String>,
pub coverage_check: String,
pub quality_proxy: String,
}
impl Default for ValidationCommands {
fn default() -> Self {
Self {
unit_tests: "cargo test".to_string(),
doctests: "cargo test --doc".to_string(),
property_tests: "cargo test --features property-tests".to_string(),
examples: vec!["cargo run --example demo".to_string()],
coverage_check: "cargo llvm-cov --fail-under-lines 80".to_string(),
quality_proxy: "pmat quality-gate --file".to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ImplementationSpecs {
pub primary_files: Vec<String>,
pub test_files: Vec<String>,
pub doc_files: Vec<String>,
pub example_files: Vec<String>,
}
impl Default for ImplementationSpecs {
fn default() -> Self {
Self {
primary_files: vec!["src/lib.rs".to_string()],
test_files: vec!["tests/integration.rs".to_string()],
doc_files: vec!["README.md".to_string()],
example_files: vec!["examples/demo.rs".to_string()],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PdmtTodoList {
pub project_name: String,
pub todos: Vec<PdmtTodo>,
pub quality_config: PdmtQualityConfig,
pub generated_at: String,
pub deterministic_seed: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PdmtQualityConfig {
pub enforcement_mode: EnforcementMode,
pub coverage_threshold: f32,
pub max_complexity: u32,
pub require_doctests: bool,
pub require_property_tests: bool,
pub require_examples: bool,
pub zero_satd_tolerance: bool,
}
impl Default for PdmtQualityConfig {
fn default() -> Self {
Self {
enforcement_mode: EnforcementMode::Strict,
coverage_threshold: 80.0,
max_complexity: 8,
require_doctests: true,
require_property_tests: true,
require_examples: true,
zero_satd_tolerance: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EnforcementMode {
Strict,
Advisory,
AutoFix,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityValidationResult {
pub overall_passed: bool,
pub detailed_results: QualityResults,
pub recommendations: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityResults {
pub structure_result: ValidationOutcome,
pub coverage_result: ValidationOutcome,
pub doctest_result: ValidationOutcome,
pub property_result: ValidationOutcome,
pub example_result: ValidationOutcome,
pub satd_result: ValidationOutcome,
pub proxy_result: ValidationOutcome,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationOutcome {
pub passed: bool,
pub message: String,
pub violations: Vec<String>,
}
impl ValidationOutcome {
#[must_use]
pub fn success(message: String) -> Self {
Self {
passed: true,
message,
violations: Vec::new(),
}
}
#[must_use]
pub fn failure(message: String, violations: Vec<String>) -> Self {
Self {
passed: false,
message,
violations,
}
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pdmt_todo_creation() {
let content = "Implement new feature".to_string();
let priority = TodoPriority::High;
let todo = PdmtTodo::new(content.clone(), priority);
assert_eq!(todo.content, content);
assert_eq!(todo.priority, TodoPriority::High);
assert_eq!(todo.status, TodoStatus::Pending);
assert_eq!(todo.estimated_hours, 4.0);
assert!(!todo.id.is_empty());
assert_eq!(todo.success_criteria.len(), 4);
assert!(todo
.success_criteria
.contains(&"Unit tests pass with >80% coverage".to_string()));
}
#[test]
fn test_pdmt_todo_default_values() {
let todo = PdmtTodo::new("Test content".to_string(), TodoPriority::Medium);
assert_eq!(todo.dependencies.len(), 0);
assert_eq!(todo.quality_gates, TodoQualityGates::default());
assert_eq!(todo.validation_commands, ValidationCommands::default());
assert_eq!(todo.implementation_specs, ImplementationSpecs::default());
}
#[test]
fn test_get_primary_file_path_with_files() {
let mut todo = PdmtTodo::new("Test".to_string(), TodoPriority::Low);
todo.implementation_specs.primary_files =
vec!["src/main.rs".to_string(), "src/lib.rs".to_string()];
assert_eq!(todo.get_primary_file_path(), "src/main.rs");
}
#[test]
fn test_get_primary_file_path_empty() {
let todo = PdmtTodo::new("Test".to_string(), TodoPriority::Low);
assert_eq!(todo.get_primary_file_path(), "src/lib.rs");
}
#[test]
fn test_validation_outcome_success() {
let outcome = ValidationOutcome::success("All tests passed".to_string());
assert!(outcome.passed);
assert_eq!(outcome.message, "All tests passed");
assert_eq!(outcome.violations.len(), 0);
}
#[test]
fn test_validation_outcome_failure() {
let violations = vec!["Missing test".to_string(), "Low coverage".to_string()];
let outcome = ValidationOutcome::failure("Tests failed".to_string(), violations.clone());
assert!(!outcome.passed);
assert_eq!(outcome.message, "Tests failed");
assert_eq!(outcome.violations, violations);
}
#[test]
fn test_todo_status_values() {
let _pending = TodoStatus::Pending;
let _in_progress = TodoStatus::InProgress;
let _completed = TodoStatus::Completed;
let _blocked = TodoStatus::Blocked;
}
#[test]
fn test_todo_priority_values() {
let _low = TodoPriority::Low;
let _medium = TodoPriority::Medium;
let _high = TodoPriority::High;
let _critical = TodoPriority::Critical;
}
#[test]
fn test_serde_roundtrip() {
let original = PdmtTodo::new("Test serialization".to_string(), TodoPriority::High);
let json = serde_json::to_string(&original).expect("Serialization failed");
let deserialized: PdmtTodo = serde_json::from_str(&json).expect("Deserialization failed");
assert_eq!(original.id, deserialized.id);
assert_eq!(original.content, deserialized.content);
assert_eq!(original.priority, deserialized.priority);
assert_eq!(original.status, deserialized.status);
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}