use crate::models::pdmt::{EnforcementMode, PdmtQualityConfig};
use crate::services::pdmt_quality_integration::PdmtQualityEnforcer;
use crate::services::pdmt_service::PdmtService;
use async_trait::async_trait;
use pmcp::{Error, RequestHandlerExtra, Result, ToolHandler};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tracing::{debug, error, info};
#[derive(Debug, Deserialize)]
pub struct PdmtInput {
pub requirements: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub project_name: Option<String>,
#[serde(default = "default_granularity")]
pub granularity: String,
#[serde(default)]
pub quality_config: QualityConfigInput,
}
fn default_granularity() -> String {
"high".to_string()
}
#[derive(Debug, Deserialize)]
pub struct QualityConfigInput {
#[serde(default = "default_enforcement_mode")]
pub enforcement_mode: String,
#[serde(default = "default_coverage_threshold")]
pub coverage_threshold: f32,
#[serde(default = "default_max_complexity")]
pub max_complexity: u32,
#[serde(default = "default_require_doctests")]
pub require_doctests: bool,
#[serde(default = "default_require_property_tests")]
pub require_property_tests: bool,
#[serde(default = "default_require_examples")]
pub require_examples: bool,
#[serde(default = "default_zero_satd_tolerance")]
pub zero_satd_tolerance: bool,
}
impl Default for QualityConfigInput {
fn default() -> Self {
Self {
enforcement_mode: default_enforcement_mode(),
coverage_threshold: default_coverage_threshold(),
max_complexity: default_max_complexity(),
require_doctests: default_require_doctests(),
require_property_tests: default_require_property_tests(),
require_examples: default_require_examples(),
zero_satd_tolerance: default_zero_satd_tolerance(),
}
}
}
fn default_enforcement_mode() -> String {
"strict".to_string()
}
fn default_coverage_threshold() -> f32 {
80.0
}
fn default_max_complexity() -> u32 {
8
}
fn default_require_doctests() -> bool {
true
}
fn default_require_property_tests() -> bool {
true
}
fn default_require_examples() -> bool {
true
}
fn default_zero_satd_tolerance() -> bool {
true
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PdmtOutput {
pub success: bool,
pub message: String,
pub todo_list: Option<serde_json::Value>,
pub quality_validation: Option<serde_json::Value>,
pub total_todos: usize,
pub estimated_total_hours: f32,
}
pub struct PdmtTool {
service: PdmtService,
quality_enforcer: PdmtQualityEnforcer,
}
impl PdmtTool {
#[must_use]
pub fn new() -> Self {
Self {
service: PdmtService::new(),
quality_enforcer: PdmtQualityEnforcer::new(),
}
}
}
impl Default for PdmtTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ToolHandler for PdmtTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling pdmt_deterministic_todos with args: {}", args);
let input: PdmtInput = serde_json::from_value(args)
.map_err(|e| Error::validation(format!("Invalid arguments: {e}")))?;
if input.requirements.is_empty() {
return Err(Error::validation("Requirements list cannot be empty"));
}
info!(
"Generating deterministic todos for {} requirements",
input.requirements.len()
);
let enforcement_mode = match input.quality_config.enforcement_mode.as_str() {
"strict" => EnforcementMode::Strict,
"advisory" => EnforcementMode::Advisory,
"auto_fix" | "autofix" => EnforcementMode::AutoFix,
_ => {
return Err(Error::validation(format!(
"Invalid enforcement mode: {}. Must be 'strict', 'advisory', or 'auto_fix'",
input.quality_config.enforcement_mode
)))
}
};
let quality_config = PdmtQualityConfig {
enforcement_mode,
coverage_threshold: input.quality_config.coverage_threshold,
max_complexity: input.quality_config.max_complexity,
require_doctests: input.quality_config.require_doctests,
require_property_tests: input.quality_config.require_property_tests,
require_examples: input.quality_config.require_examples,
zero_satd_tolerance: input.quality_config.zero_satd_tolerance,
};
let todo_list = self
.service
.generate_todos(
input.requirements,
input.project_name,
&input.granularity,
quality_config,
)
.map_err(|e| Error::internal(format!("Failed to generate todos: {e}")))?;
let quality_validation = self
.quality_enforcer
.enforce_quality_standards(&todo_list)
.await
.map_err(|e| Error::internal(format!("Quality validation failed: {e}")))?;
let total_todos = todo_list.todos.len();
let estimated_total_hours: f32 = todo_list.todos.iter().map(|t| t.estimated_hours).sum();
let output =
PdmtOutput {
success: quality_validation.overall_passed,
message: if quality_validation.overall_passed {
format!(
"Successfully generated {total_todos} deterministic todos with quality enforcement"
)
} else {
format!(
"Generated {total_todos} todos but quality validation failed. Review violations."
)
},
todo_list: Some(serde_json::to_value(&todo_list).map_err(|e| {
Error::internal(format!("Failed to serialize todo list: {e}"))
})?),
quality_validation: Some(serde_json::to_value(&quality_validation).map_err(
|e| Error::internal(format!("Failed to serialize validation results: {e}")),
)?),
total_todos,
estimated_total_hours,
};
if quality_validation.overall_passed {
info!(
"Successfully generated {} todos totaling {} hours",
total_todos, estimated_total_hours
);
} else {
error!(
"Quality validation failed with {} recommendations",
quality_validation.recommendations.len()
);
}
serde_json::to_value(output)
.map_err(|e| Error::internal(format!("Serialization error: {e}")))
}
}
#[cfg(test)]
mod tests {
use crate::services::pdmt_service::PdmtService;
#[test]
fn test_pdmt_service_basic() {
let service = PdmtService::new();
let requirements = vec![
"implement user authentication".to_string(),
"add logging system".to_string(),
];
let config = crate::models::pdmt::PdmtQualityConfig::default();
let result = service
.generate_todos(
requirements,
Some("test_project".to_string()),
"medium",
config,
)
.unwrap();
assert!(!result.todos.is_empty());
assert_eq!(result.project_name, "test_project");
}
#[tokio::test]
async fn test_pdmt_quality_enforcement() {
let enforcer = crate::services::pdmt_quality_integration::PdmtQualityEnforcer::new();
let todo = crate::models::pdmt::PdmtTodo::new(
"Implement user authentication".to_string(),
crate::models::pdmt::TodoPriority::High,
);
let todo_list = crate::models::pdmt::PdmtTodoList {
project_name: "test".to_string(),
todos: vec![todo],
quality_config: crate::models::pdmt::PdmtQualityConfig::default(),
generated_at: "2024-01-01".to_string(),
deterministic_seed: 42,
};
let result = enforcer
.enforce_quality_standards(&todo_list)
.await
.unwrap();
assert!(result.overall_passed);
}
}
#[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);
}
}
}