use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ReleaseVersion {
pub version: String,
pub date: String,
pub release_type: String,
pub breaking_changes: Vec<String>,
pub features: Vec<String>,
pub bug_fixes: Vec<String>,
pub improvements: Vec<String>,
}
impl ReleaseVersion {
pub fn new(version: String, date: String, release_type: String) -> Self {
Self {
version,
date,
release_type,
breaking_changes: Vec::new(),
features: Vec::new(),
bug_fixes: Vec::new(),
improvements: Vec::new(),
}
}
pub fn add_feature(&mut self, feature: String) {
self.features.push(feature);
}
pub fn add_improvement(&mut self, improvement: String) {
self.improvements.push(improvement);
}
pub fn to_release_notes(&self) -> String {
let mut notes = format!(
"# Release {}\n\n**Date:** {}\n**Type:** {}\n\n",
self.version, self.date, self.release_type
);
if !self.breaking_changes.is_empty() {
notes.push_str("## ⚠️ Breaking Changes\n\n");
for change in &self.breaking_changes {
notes.push_str(&format!("- {}\n", change));
}
notes.push('\n');
}
if !self.features.is_empty() {
notes.push_str("## ✨ Features\n\n");
for feature in &self.features {
notes.push_str(&format!("- {}\n", feature));
}
notes.push('\n');
}
if !self.improvements.is_empty() {
notes.push_str("## 📈 Improvements\n\n");
for improvement in &self.improvements {
notes.push_str(&format!("- {}\n", improvement));
}
notes.push('\n');
}
if !self.bug_fixes.is_empty() {
notes.push_str("## 🐛 Bug Fixes\n\n");
for fix in &self.bug_fixes {
notes.push_str(&format!("- {}\n", fix));
}
notes.push('\n');
}
notes
}
}
pub struct ChangelogGenerator {
releases: Vec<ReleaseVersion>,
}
impl Default for ChangelogGenerator {
fn default() -> Self {
Self::new()
}
}
impl ChangelogGenerator {
pub fn new() -> Self {
Self {
releases: Vec::new(),
}
}
pub fn add_release(&mut self, release: ReleaseVersion) {
self.releases.push(release);
}
pub fn generate(&self) -> String {
let mut changelog = "# Changelog\n\n".to_string();
changelog.push_str("All notable changes to Sentri are documented in this file.\n\n");
for release in &self.releases {
changelog.push_str(&release.to_release_notes());
changelog.push('\n');
}
changelog
}
pub fn get_release(&self, version: &str) -> Option<&ReleaseVersion> {
self.releases.iter().find(|r| r.version == version)
}
}
pub struct APIDocGenerator {
modules: HashMap<String, ModuleDoc>,
}
#[derive(Debug, Clone)]
pub struct ModuleDoc {
pub name: String,
pub description: String,
pub exports: Vec<ExportDoc>,
}
#[derive(Debug, Clone)]
pub struct ExportDoc {
pub name: String,
pub doc: String,
pub since_version: String,
}
impl Default for APIDocGenerator {
fn default() -> Self {
Self::new()
}
}
impl APIDocGenerator {
pub fn new() -> Self {
Self {
modules: HashMap::new(),
}
}
pub fn add_module(&mut self, module: ModuleDoc) {
self.modules.insert(module.name.clone(), module);
}
pub fn generate(&self) -> String {
let mut doc = "# Sentri API Reference\n\n".to_string();
for module in self.modules.values() {
doc.push_str(&format!("## {}\n\n", module.name));
doc.push_str(&format!("{}\n\n", module.description));
if !module.exports.is_empty() {
doc.push_str("### Exports\n\n");
for export in &module.exports {
doc.push_str(&format!(
"#### `{}`\n\n{}\n\n**Since:** {}\n\n",
export.name, export.doc, export.since_version
));
}
}
}
doc
}
}
pub fn generate_v0_3_0_changelog() -> String {
let mut generator = ChangelogGenerator::new();
let mut v0_3_0 = ReleaseVersion::new(
"0.3.0".to_string(),
"2024-Q2".to_string(),
"minor".to_string(),
);
v0_3_0.add_feature("25 new vulnerability detectors across EVM, Solana, Move".to_string());
v0_3_0.add_feature("Phase A: 5 critical detectors (health_check, merkle_root, dvn_single_point, synthetic_mint, lst_depeg)".to_string());
v0_3_0.add_feature("Phase B: 8 high-priority detectors (oracle_self_trade, solana_durable_nonce, synthetic_collateral_oracle, erc4626_inflation_protection, arbitrary_call_msg_value, reentrancy_via_whitelisted, proxy_storage_collision, bridge_address_cryptographic_verify)".to_string());
v0_3_0.add_feature("Phase C: 12 medium-priority detectors across all chains".to_string());
v0_3_0.add_feature("OpenZeppelin integration module for audit mapping".to_string());
v0_3_0.add_feature("Runtime fuzzers for detector validation".to_string());
v0_3_0.add_feature("Comprehensive test infrastructure with quality metrics".to_string());
v0_3_0.add_feature("Multi-format report generation (Markdown, JSON, HTML, CSV)".to_string());
v0_3_0.add_feature("Integration testing against real exploits (25+ H-codes)".to_string());
v0_3_0.add_improvement(
"Detector architecture optimized for speed (~1-2ms per detector)".to_string(),
);
v0_3_0.add_improvement("False positive minimization through pattern refinement".to_string());
v0_3_0.add_improvement(
"Full metadata tracking for all findings (exploit_id, loss, year, vulnerability_type)"
.to_string(),
);
v0_3_0.add_improvement("Cross-chain consistency in detector patterns".to_string());
v0_3_0
.add_improvement("4-5 comprehensive tests per detector (50+ total test cases)".to_string());
generator.add_release(v0_3_0);
generator.generate()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn release_version_structure() {
let mut release = ReleaseVersion::new(
"1.0.0".to_string(),
"2024-01-01".to_string(),
"major".to_string(),
);
release.add_feature("New feature".to_string());
assert_eq!(release.features.len(), 1);
}
#[test]
fn changelog_generator() {
let mut gen = ChangelogGenerator::new();
let release = ReleaseVersion::new(
"0.1.0".to_string(),
"2024-01-01".to_string(),
"initial".to_string(),
);
gen.add_release(release);
let changelog = gen.generate();
assert!(changelog.contains("0.1.0"));
}
#[test]
fn api_doc_generator() {
let mut gen = APIDocGenerator::new();
let module = ModuleDoc {
name: "detectors".to_string(),
description: "Vulnerability detectors".to_string(),
exports: vec![ExportDoc {
name: "detect_reentrancy".to_string(),
doc: "Detects reentrancy vulnerabilities".to_string(),
since_version: "0.1.0".to_string(),
}],
};
gen.add_module(module);
let doc = gen.generate();
assert!(doc.contains("detect_reentrancy"));
}
#[test]
fn v0_3_0_changelog() {
let changelog = generate_v0_3_0_changelog();
assert!(changelog.contains("0.3.0"));
assert!(changelog.contains("25 new vulnerability detectors"));
}
}