4 stable releases
| 1.0.3 | Feb 1, 2026 |
|---|---|
| 1.0.2 | Dec 15, 2025 |
| 1.0.1 | Dec 10, 2025 |
| 1.0.0 | Dec 9, 2025 |
| 0.1.0 |
|
#21 in #sarif
Used in sarif-to-md
31KB
425 lines
sarif-to-md-core
Core Rust library for parsing SARIF (Static Analysis Results Interchange Format) reports and converting them to Markdown.
Features
- SARIF Parsing - Robust parsing of SARIF v2.1.0 format
- Markdown Generation - Flexible template-based Markdown generation
- Multiple Formats - GitHub Flavored Markdown and CommonMark support
- Extensible - Builder pattern for easy customization
- Type-Safe - Strongly typed API with comprehensive error handling
- Zero-Copy - Efficient parsing with minimal allocations
Installation
Add to your Cargo.toml:
[dependencies]
sarif-to-md-core = "0.1"
Quick Start
use sarif_to_md_core::{
ReportProcessorBuilder,
generators::SarifMarkdownGenerator,
markdown::MarkdownFormat,
};
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Read SARIF content
let sarif_content = fs::read_to_string("security-report.sarif")?;
// Build processor
let processor = ReportProcessorBuilder::new()
.generator(SarifMarkdownGenerator::new(
MarkdownFormat::GitHubFlavored,
true // with emoji
))
.content(sarif_content)
.build()?;
// Generate Markdown
let markdown = processor.generate()?;
println!("{}", markdown);
Ok(())
}
API Overview
Core Types
ReportProcessor<G>
Main processor for generating Markdown from SARIF reports.
pub struct ReportProcessor<G: MarkdownGenerator> {
generator: G,
content: String,
}
impl<G: MarkdownGenerator> ReportProcessor<G> {
pub fn new(generator: G, content: String) -> Self;
pub fn generate(self) -> Result<String, Error>;
}
ReportProcessorBuilder<G>
Builder for constructing ReportProcessor instances.
impl ReportProcessorBuilder<()> {
pub fn new() -> Self;
pub fn generator<G: MarkdownGenerator>(self, generator: G)
-> ReportProcessorBuilder<G>;
}
impl<G: MarkdownGenerator> ReportProcessorBuilder<G> {
pub fn content(self, content: String) -> Self;
pub fn build(self) -> Result<ReportProcessor<G>, BuilderError>;
}
MarkdownGenerator Trait
Core trait for implementing custom Markdown generators.
pub trait MarkdownGenerator {
type Input: DeserializeOwned;
fn generate_markdown_template(&self, report: &Self::Input)
-> Result<String, GeneratorError>;
}
Generators
SarifMarkdownGenerator
Built-in generator for SARIF reports.
impl SarifMarkdownGenerator {
pub fn new(
markdown_format: MarkdownFormat,
with_emoji: bool
) -> Self;
}
Markdown Formats
pub enum MarkdownFormat {
CommonMark, // Standard Markdown
GitHubFlavored, // GitHub Flavored Markdown with HTML
}
Error Types
pub enum Error {
JsonError(serde_json::Error),
GeneratorError(GeneratorError),
IoError(std::io::Error),
}
pub enum GeneratorError {
TemplateError(askama::Error),
}
pub enum BuilderError {
MissingContent,
}
Advanced Usage
Custom Generator Implementation
Create your own Markdown generator by implementing the MarkdownGenerator trait:
use sarif_to_md_core::{
markdown::{MarkdownGenerator, GeneratorError},
ReportProcessorBuilder,
};
use serde::Deserialize;
#[derive(Deserialize)]
struct CustomReport {
findings: Vec<Finding>,
}
struct CustomMarkdownGenerator;
impl MarkdownGenerator for CustomMarkdownGenerator {
type Input = CustomReport;
fn generate_markdown_template(&self, report: &Self::Input)
-> Result<String, GeneratorError>
{
let mut markdown = String::from("# Custom Report\n\n");
for finding in &report.findings {
markdown.push_str(&format!("- {}\n", finding.title));
}
Ok(markdown)
}
}
// Use it
let processor = ReportProcessorBuilder::new()
.generator(CustomMarkdownGenerator)
.content(json_content)
.build()?;
Customizing Output Format
use sarif_to_md_core::{
generators::SarifMarkdownGenerator,
markdown::MarkdownFormat,
};
// GitHub Flavored with collapsible sections
let gfm_generator = SarifMarkdownGenerator::new(
MarkdownFormat::GitHubFlavored,
true // emoji enabled
);
// CommonMark for maximum compatibility
let cm_generator = SarifMarkdownGenerator::new(
MarkdownFormat::CommonMark,
false // no emoji
);
Error Handling
use sarif_to_md_core::{Error, BuilderError};
match processor.generate() {
Ok(markdown) => println!("{}", markdown),
Err(Error::JsonError(e)) => eprintln!("Invalid JSON: {}", e),
Err(Error::GeneratorError(e)) => eprintln!("Template error: {}", e),
Err(Error::IoError(e)) => eprintln!("IO error: {}", e),
}
Processing Multiple Files
use std::path::Path;
use sarif_to_md_core::{
ReportProcessorBuilder,
generators::SarifMarkdownGenerator,
markdown::MarkdownFormat,
};
fn process_directory(dir: &Path) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let mut reports = Vec::new();
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
if entry.path().extension().and_then(|s| s.to_str()) == Some("sarif") {
let content = std::fs::read_to_string(entry.path())?;
let processor = ReportProcessorBuilder::new()
.generator(SarifMarkdownGenerator::new(
MarkdownFormat::CommonMark,
false
))
.content(content)
.build()?;
reports.push(processor.generate()?);
}
}
Ok(reports)
}
Template System
The library uses Askama for template rendering. Templates are located in templates/sarif/:
report.md- Main report templatemacros.md- Reusable template macros
Template Variables
Templates have access to:
struct SarifReportTemplate {
runs: Vec<SarifRun>,
timestamp: String,
with_emoji: bool,
is_gfm: bool,
}
struct SarifRun {
tool_name: String,
tool_version: Option<String>,
total_results: usize,
severity_counts: Vec<SeverityCount>,
results: Vec<SarifResultView>,
}
Testing
# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Test specific module
cargo test markdown::sarif
Benchmarking
# Run benchmarks (requires nightly)
cargo +nightly bench
Documentation
Generate and open the full API documentation:
cargo doc --no-deps --open
MSRV (Minimum Supported Rust Version)
This crate requires Rust 1.90 or later.
Dependencies
serde- Serialization frameworkserde_json- JSON parsingserde-sarif- SARIF format supportaskama- Template enginechrono- Date/time handlingthiserror- Error handling
Performance Considerations
- Parsing: O(n) complexity for SARIF parsing
- Template Rendering: O(m) where m is the number of findings
- Memory: Minimal allocations, most data is borrowed
- Throughput: Can process 1000+ findings per second on modern hardware
Architecture
sarif-to-md-core/
├── src/
│ ├── lib.rs # Public API
│ ├── error.rs # Error types
│ ├── generators.rs # Re-exports
│ └── markdown/
│ ├── mod.rs # Core traits
│ └── sarif/
│ ├── generator.rs # SARIF generator
│ └── types.rs # View models
└── templates/
└── sarif/
├── report.md # Main template
└── macros.md # Helper macros
Comparison with Alternatives
| Feature | sarif-to-md-core | Other Tools |
|---|---|---|
| Language | Rust | JavaScript/Python |
| Performance | High | Medium |
| Memory Usage | Low | Medium-High |
| Type Safety | Strong | Weak |
| Dependencies | Minimal | Many |
Contributing
See the parent repository's CONTRIBUTING.md for guidelines.
Changelog
See CHANGELOG.md for version history.
License
This project is dual-licensed under:
- MIT License (LICENSE-MIT)
- Apache License 2.0 (LICENSE-APACHE)
Choose the license that best suits your needs.
Resources
Support
Dependencies
~3.5–5MB
~102K SLoC