pub mod adapter;
pub mod cli_impl;
pub mod cli_mapping;
pub mod http_impl;
pub mod mcp_mapping;
pub mod mcp_simple;
pub mod real_service;
pub mod service;
pub mod simple_service;
#[cfg(test)]
mod tests;
pub mod uniform_cli_commands;
pub mod versioning;
use crate::utils::path_validator::PathValidator;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ContractError {
#[error("Path not found: {0}")]
PathNotFound(PathBuf),
#[error("Missing required parameter: {0}")]
MissingParam(&'static str),
#[error("Invalid timeout value")]
InvalidTimeout,
#[error("Too many files requested: {0} (max: 1000)")]
TooManyFiles(usize),
#[error("Invalid parameter value: {0}")]
InvalidValue(String),
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub enum OutputFormat {
#[default]
Table,
Json,
Yaml,
Markdown,
Csv,
Summary,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd)]
#[serde(rename_all = "lowercase")]
pub enum SatdSeverity {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct BaseAnalysisContract {
pub path: PathBuf,
pub format: OutputFormat,
pub output: Option<PathBuf>,
pub top_files: Option<usize>,
pub include_tests: bool,
pub timeout: u64,
}
impl Default for BaseAnalysisContract {
fn default() -> Self {
Self {
path: PathBuf::from("."),
format: OutputFormat::default(),
output: None,
top_files: Some(10),
include_tests: false,
timeout: 60,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AnalyzeComplexityContract {
#[serde(flatten)]
pub base: BaseAnalysisContract,
pub max_cyclomatic: Option<u32>,
pub max_cognitive: Option<u32>,
pub max_halstead: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AnalyzeSatdContract {
#[serde(flatten)]
pub base: BaseAnalysisContract,
pub severity: Option<SatdSeverity>,
pub critical_only: bool,
pub strict: bool,
pub fail_on_violation: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AnalyzeDeadCodeContract {
#[serde(flatten)]
pub base: BaseAnalysisContract,
pub include_unreachable: bool,
pub min_dead_lines: usize,
pub max_percentage: f64,
pub fail_on_violation: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AnalyzeTdgContract {
#[serde(flatten)]
pub base: BaseAnalysisContract,
pub threshold: f64,
pub include_components: bool,
pub critical_only: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AnalyzeLintHotspotContract {
#[serde(flatten)]
pub base: BaseAnalysisContract,
pub file: Option<PathBuf>,
pub max_density: f64,
pub min_confidence: f64,
pub enforce: bool,
pub dry_run: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AnalyzeEntropyContract {
#[serde(flatten)]
pub base: BaseAnalysisContract,
pub min_severity: Option<String>,
pub top_violations: Option<usize>,
pub file: Option<PathBuf>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct QualityGateContract {
#[serde(flatten)]
pub base: BaseAnalysisContract,
pub profile: QualityProfile,
pub file: Option<PathBuf>,
pub fail_on_violation: bool,
pub verbose: bool,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub enum QualityProfile {
#[default]
Standard,
Strict,
Extreme,
Toyota,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RefactorAutoContract {
pub file: PathBuf,
pub format: OutputFormat,
pub output: Option<PathBuf>,
pub target_complexity: u32,
pub dry_run: bool,
pub timeout: u64,
}
pub trait ContractValidation {
fn validate(&self) -> Result<(), ContractError>;
}
impl ContractValidation for BaseAnalysisContract {
fn validate(&self) -> Result<(), ContractError> {
PathValidator::ensure_exists(&self.path)
.map_err(|_| ContractError::PathNotFound(self.path.clone()))?;
if self.timeout == 0 {
return Err(ContractError::InvalidTimeout);
}
if let Some(top_files) = self.top_files {
if top_files > 1000 {
return Err(ContractError::TooManyFiles(top_files));
}
}
Ok(())
}
}
impl ContractValidation for AnalyzeComplexityContract {
fn validate(&self) -> Result<(), ContractError> {
self.base.validate()?;
if let Some(max_halstead) = self.max_halstead {
if max_halstead <= 0.0 {
return Err(ContractError::InvalidValue(
"max_halstead must be positive".into(),
));
}
}
Ok(())
}
}
impl ContractValidation for AnalyzeSatdContract {
fn validate(&self) -> Result<(), ContractError> {
self.base.validate()
}
}
impl ContractValidation for AnalyzeDeadCodeContract {
fn validate(&self) -> Result<(), ContractError> {
self.base.validate()?;
if self.max_percentage < 0.0 || self.max_percentage > 100.0 {
return Err(ContractError::InvalidValue(
"max_percentage must be 0-100".into(),
));
}
Ok(())
}
}
impl ContractValidation for AnalyzeTdgContract {
fn validate(&self) -> Result<(), ContractError> {
self.base.validate()?;
if self.threshold < 0.0 {
return Err(ContractError::InvalidValue(
"threshold must be non-negative".into(),
));
}
Ok(())
}
}
impl ContractValidation for AnalyzeLintHotspotContract {
fn validate(&self) -> Result<(), ContractError> {
self.base.validate()?;
if self.max_density < 0.0 {
return Err(ContractError::InvalidValue(
"max_density must be non-negative".into(),
));
}
if self.min_confidence < 0.0 || self.min_confidence > 1.0 {
return Err(ContractError::InvalidValue(
"min_confidence must be 0-1".into(),
));
}
Ok(())
}
}
impl ContractValidation for AnalyzeEntropyContract {
fn validate(&self) -> Result<(), ContractError> {
self.base.validate()?;
if let Some(severity) = &self.min_severity {
match severity.as_str() {
"low" | "medium" | "high" => {}
_ => {
return Err(ContractError::InvalidValue(
"min_severity must be 'low', 'medium', or 'high'".into(),
))
}
}
}
if let Some(violations) = self.top_violations {
if violations > 1000 {
return Err(ContractError::TooManyFiles(violations));
}
}
if let Some(file) = &self.file {
PathValidator::ensure_exists(file)
.map_err(|_| ContractError::PathNotFound(file.clone()))?;
}
Ok(())
}
}
impl ContractValidation for QualityGateContract {
fn validate(&self) -> Result<(), ContractError> {
self.base.validate()?;
if let Some(file) = &self.file {
PathValidator::ensure_exists(file)
.map_err(|_| ContractError::PathNotFound(file.clone()))?;
}
Ok(())
}
}
impl ContractValidation for RefactorAutoContract {
fn validate(&self) -> Result<(), ContractError> {
PathValidator::ensure_file(&self.file)
.map_err(|_| ContractError::PathNotFound(self.file.clone()))?;
if self.timeout == 0 {
return Err(ContractError::InvalidTimeout);
}
if self.target_complexity == 0 {
return Err(ContractError::InvalidValue(
"target_complexity must be > 0".into(),
));
}
Ok(())
}
}
#[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);
}
}
}