use super::claims::{Claim, ClaimConfidence, ClaimType};
use super::verify::VerifyResult;
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum AuditTaskStatus {
Verified,
Broken,
NeedsVerification,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct AuditTask {
pub doc: String,
pub line: usize,
pub claim: String,
#[serde(rename = "type")]
pub claim_type: ClaimType,
pub claim_value: String,
pub confidence: ClaimConfidence,
pub status: AuditTaskStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub action: Option<String>,
}
pub fn build_task(claim: Claim, result: VerifyResult) -> AuditTask {
let (status, action) = match result {
VerifyResult::Verified => (AuditTaskStatus::Verified, None),
VerifyResult::Broken { suggestion } => (AuditTaskStatus::Broken, suggestion),
VerifyResult::NeedsVerification { hint } => {
(AuditTaskStatus::NeedsVerification, Some(hint))
}
};
let claim_description = build_claim_description(&claim);
AuditTask {
doc: claim.doc_file,
line: claim.line,
claim: claim_description,
claim_type: claim.claim_type,
claim_value: claim.value,
confidence: claim.confidence,
status,
action,
}
}
fn build_claim_description(claim: &Claim) -> String {
match claim.claim_type {
ClaimType::FilePath => format!("file path `{}`", claim.value),
ClaimType::DirectoryPath => format!("directory path `{}`", claim.value),
ClaimType::CodeExample => {
let preview = if claim.value.len() > 50 {
format!("{}...", &claim.value[..50])
} else {
claim.value.clone()
};
format!("code example: {}", preview.replace('\n', " "))
}
ClaimType::ClassName => format!("class reference `{}`", claim.value),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_verified_task() {
let claim = Claim {
claim_type: ClaimType::FilePath,
value: "/src/main.rs".to_string(),
doc_file: "docs/index.md".to_string(),
line: 10,
confidence: ClaimConfidence::Real,
context: None,
};
let task = build_task(claim, VerifyResult::Verified);
assert_eq!(task.status, AuditTaskStatus::Verified);
assert!(task.action.is_none());
assert_eq!(task.claim, "file path `/src/main.rs`");
}
#[test]
fn test_build_broken_task() {
let claim = Claim {
claim_type: ClaimType::FilePath,
value: "/src/old.rs".to_string(),
doc_file: "docs/index.md".to_string(),
line: 15,
confidence: ClaimConfidence::Real,
context: None,
};
let task = build_task(
claim,
VerifyResult::Broken {
suggestion: Some("File was renamed to /src/new.rs".to_string()),
},
);
assert_eq!(task.status, AuditTaskStatus::Broken);
assert!(task.action.is_some());
assert!(task.action.unwrap().contains("renamed"));
}
#[test]
fn test_build_needs_verification_task() {
let claim = Claim {
claim_type: ClaimType::CodeExample,
value: "fn process() { }".to_string(),
doc_file: "docs/api.md".to_string(),
line: 42,
confidence: ClaimConfidence::Unclear,
context: None,
};
let task = build_task(
claim,
VerifyResult::NeedsVerification {
hint: "Verify code example syntax matches current API.".to_string(),
},
);
assert_eq!(task.status, AuditTaskStatus::NeedsVerification);
assert!(task.action.is_some());
}
#[test]
fn test_claim_description_truncation() {
let long_code = "function example() {\n // This is a very long code example that should be truncated\n return true;\n}";
let claim = Claim {
claim_type: ClaimType::CodeExample,
value: long_code.to_string(),
doc_file: "docs/test.md".to_string(),
line: 1,
confidence: ClaimConfidence::Unclear,
context: None,
};
let description = build_claim_description(&claim);
assert!(description.contains("..."));
assert!(!description.contains('\n'));
}
}