Skip to main content

veritas_plugin_api/
lib.rs

1use std::path::Path;
2
3use anyhow::Result;
4use camino::Utf8PathBuf;
5use serde::{Deserialize, Serialize};
6
7pub trait LanguagePlugin: Send + Sync {
8    fn id(&self) -> &'static str;
9
10    fn display_name(&self) -> &'static str;
11
12    fn detect_project(&self, root: &Path) -> Result<ProjectInfo>;
13
14    fn discover_targets(&self, root: &Path) -> Result<Vec<VerificationTarget>>;
15
16    fn generate_tests(
17        &self,
18        target: &VerificationTarget,
19        plan: &VerificationPlan,
20    ) -> Result<Vec<GeneratedArtifact>>;
21
22    fn run_tests(
23        &self,
24        root: &Path,
25        artifacts: &[GeneratedArtifact],
26        plan: &VerificationPlan,
27    ) -> Result<TestRunResult>;
28
29    fn collect_coverage(&self, root: &Path) -> Result<Option<CoverageReport>>;
30}
31
32pub trait VerificationPlanner: Send + Sync {
33    fn plan(&self, project: &ProjectInfo, target: &VerificationTarget) -> Result<VerificationPlan>;
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
37pub struct ProjectInfo {
38    pub language: String,
39    pub name: String,
40    pub root: Utf8PathBuf,
41    pub manifests: Vec<Utf8PathBuf>,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
45pub struct VerificationTarget {
46    pub id: String,
47    pub language: String,
48    pub kind: TargetKind,
49    pub path: Utf8PathBuf,
50    pub symbol: Option<String>,
51    pub signature: Option<String>,
52    pub line_range: Option<LineRange>,
53    pub description: String,
54    pub risk: RiskLevel,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
58pub struct LineRange {
59    pub start: usize,
60    pub end: usize,
61}
62
63impl LineRange {
64    pub fn overlaps(&self, other: &LineRange) -> bool {
65        self.start <= other.end && other.start <= self.end
66    }
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
70#[serde(rename_all = "snake_case")]
71pub enum TargetKind {
72    Project,
73    Package,
74    File,
75    Function,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
79#[serde(rename_all = "snake_case")]
80pub enum RiskLevel {
81    Low,
82    Medium,
83    High,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
87#[serde(rename_all = "snake_case")]
88pub enum FailureSeverity {
89    Info,
90    Warning,
91    Error,
92    Critical,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
96pub struct VerificationPlan {
97    pub target_id: String,
98    pub strategies: Vec<VerificationStrategy>,
99    pub budget_seconds: u64,
100    pub write_generated_tests: bool,
101    pub run_existing_tests: bool,
102    pub run_generated_tests: bool,
103    pub fail_on_generated_test_failure: bool,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
107#[serde(rename_all = "snake_case")]
108pub enum VerificationStrategy {
109    ExistingTests,
110    UnitTests,
111    PropertyTests,
112    Fuzzing,
113    DifferentialTests,
114    MutationChecks,
115    CoverageFeedback,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
119pub struct GeneratedArtifact {
120    pub id: String,
121    pub language: String,
122    pub kind: ArtifactKind,
123    pub target_id: String,
124    pub path: Utf8PathBuf,
125    pub contents: String,
126    pub description: String,
127    pub status: ArtifactStatus,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
131#[serde(rename_all = "snake_case")]
132pub enum ArtifactKind {
133    UnitTest,
134    PropertyTest,
135    FuzzHarness,
136    HarnessIndex,
137    MutationCheck,
138    CoverageFeedback,
139    DifferentialBaseline,
140    ReproCase,
141    PackageAwareness,
142    PackageGraph,
143    SymbolGraph,
144    ChangeDigest,
145    AiFeedback,
146    CandidatePatch,
147    FindingBaseline,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
151#[serde(rename_all = "snake_case")]
152pub enum ArtifactStatus {
153    Planned,
154    Written,
155    Skipped,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
159pub struct TestRunResult {
160    pub language: String,
161    pub status: RunStatus,
162    pub commands: Vec<CommandRecord>,
163    pub failures: Vec<Failure>,
164    pub duration_ms: u128,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
168#[serde(rename_all = "snake_case")]
169pub enum RunStatus {
170    Passed,
171    Failed,
172    Skipped,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
176pub struct CommandRecord {
177    pub program: String,
178    pub args: Vec<String>,
179    pub cwd: Utf8PathBuf,
180    pub exit_code: Option<i32>,
181    pub status: RunStatus,
182    pub stdout: String,
183    pub stderr: String,
184    pub duration_ms: u128,
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
188pub struct Failure {
189    #[serde(default, skip_serializing_if = "Option::is_none")]
190    pub id: Option<String>,
191    pub message: String,
192    #[serde(default = "default_failure_severity")]
193    pub severity: FailureSeverity,
194    pub target_id: Option<String>,
195    pub artifact_id: Option<String>,
196    pub command: String,
197    pub stdout_excerpt: String,
198    pub stderr_excerpt: String,
199    pub repro: Option<ReproCase>,
200}
201
202fn default_failure_severity() -> FailureSeverity {
203    FailureSeverity::Error
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
207pub struct ReproCase {
208    pub command: String,
209    pub input: Option<String>,
210    pub path: Option<Utf8PathBuf>,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
214pub struct CoverageReport {
215    pub tool: String,
216    pub summary: String,
217    pub files: Vec<CoverageFile>,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
221pub struct CoverageFile {
222    pub path: Utf8PathBuf,
223    pub line_coverage_percent: Option<u8>,
224    pub uncovered_ranges: Vec<String>,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
228pub struct VerificationReport {
229    pub project: Option<ProjectInfo>,
230    pub targets: Vec<VerificationTarget>,
231    pub plan: Option<VerificationPlan>,
232    pub artifacts: Vec<GeneratedArtifact>,
233    pub runs: Vec<TestRunResult>,
234    pub coverage: Vec<CoverageReport>,
235    pub findings: Vec<Failure>,
236    pub suggested_next_steps: Vec<String>,
237}
238
239impl VerificationReport {
240    pub fn empty() -> Self {
241        Self {
242            project: None,
243            targets: Vec::new(),
244            plan: None,
245            artifacts: Vec::new(),
246            runs: Vec::new(),
247            coverage: Vec::new(),
248            findings: Vec::new(),
249            suggested_next_steps: Vec::new(),
250        }
251    }
252}