use crate::models::proxy::{ProxyMode, ProxyOperation, ProxyRequest, QualityConfig};
use crate::services::quality_proxy::QualityProxyService;
use async_trait::async_trait;
use pmcp::{Error, RequestHandlerExtra, Result, ToolHandler};
use serde::Deserialize;
use serde_json::Value;
use tracing::{debug, info};
#[derive(Debug, Deserialize)]
pub struct QualityProxyInput {
pub operation: String,
pub file_path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub old_content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub new_content: Option<String>,
#[serde(default = "default_mode")]
pub mode: String,
#[serde(default)]
pub quality_config: QualityConfigInput,
}
fn default_mode() -> String {
"strict".to_string()
}
#[derive(Debug, Deserialize)]
pub struct QualityConfigInput {
#[serde(default = "default_max_complexity")]
pub max_complexity: u32,
#[serde(default = "default_allow_satd")]
pub allow_satd: bool,
#[serde(default = "default_require_docs")]
pub require_docs: bool,
#[serde(default = "default_auto_format")]
pub auto_format: bool,
}
impl Default for QualityConfigInput {
fn default() -> Self {
Self {
max_complexity: default_max_complexity(),
allow_satd: default_allow_satd(),
require_docs: default_require_docs(),
auto_format: default_auto_format(),
}
}
}
fn default_max_complexity() -> u32 {
20
}
fn default_allow_satd() -> bool {
false
}
fn default_require_docs() -> bool {
true
}
fn default_auto_format() -> bool {
true
}
pub struct QualityProxyTool;
#[async_trait]
impl ToolHandler for QualityProxyTool {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
debug!("Handling quality_proxy with args: {}", args);
let input: QualityProxyInput = serde_json::from_value(args)
.map_err(|e| Error::validation(format!("Invalid arguments: {e}")))?;
info!("Processing quality proxy request for {}", input.file_path);
debug!("Proxy mode: {}, Operation: {}", input.mode, input.operation);
let operation = match input.operation.as_str() {
"write" => ProxyOperation::Write,
"edit" => ProxyOperation::Edit,
"append" => ProxyOperation::Append,
_ => {
return Err(Error::validation(format!(
"Invalid operation: {}",
input.operation
)))
}
};
let mode = match input.mode.as_str() {
"strict" => ProxyMode::Strict,
"advisory" => ProxyMode::Advisory,
"auto_fix" | "auto-fix" => ProxyMode::AutoFix,
_ => return Err(Error::validation(format!("Invalid mode: {}", input.mode))),
};
let quality_config = QualityConfig {
max_complexity: input.quality_config.max_complexity,
allow_satd: input.quality_config.allow_satd,
require_docs: input.quality_config.require_docs,
auto_format: input.quality_config.auto_format,
};
let request = ProxyRequest {
operation,
file_path: input.file_path.clone(),
content: input.content,
old_content: input.old_content,
new_content: input.new_content,
mode,
quality_config,
};
let service = QualityProxyService::new();
let response = service
.proxy_operation(request)
.await
.map_err(|e| Error::internal(format!("Failed to process request: {e}")))?;
let result = serde_json::to_value(response)
.map_err(|e| Error::internal(format!("Failed to serialize response: {e}")))?;
info!("Quality proxy request completed");
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_quality_config() {
let config = QualityConfigInput::default();
assert_eq!(config.max_complexity, default_max_complexity());
assert_eq!(config.allow_satd, default_allow_satd());
assert_eq!(config.require_docs, default_require_docs());
assert_eq!(config.auto_format, default_auto_format());
}
#[tokio::test]
#[ignore = "Requires pmcp internal types"]
async fn test_quality_proxy_handle() {
}
}
#[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);
}
}
}