| Crates.io | sarif-to-md-core |
| lib.rs | sarif-to-md-core |
| version | 1.0.2 |
| created_at | 2025-12-09 19:41:53.297242+00 |
| updated_at | 2025-12-15 08:42:38.218723+00 |
| description | Core library for converting SARIF security reports to Markdown. |
| homepage | https://github.com/fulgas/sarif-to-md-rs |
| repository | https://github.com/fulgas/sarif-to-md-rs |
| max_upload_size | |
| id | 1976130 |
| size | 133,153 |
Core Rust library for parsing SARIF (Static Analysis Results Interchange Format) reports and converting them to Markdown.
Add to your Cargo.toml:
[dependencies]
sarif-to-md-core = "0.1"
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(())
}
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 TraitCore trait for implementing custom Markdown generators.
pub trait MarkdownGenerator {
type Input: DeserializeOwned;
fn generate_markdown_template(&self, report: &Self::Input)
-> Result<String, GeneratorError>;
}
SarifMarkdownGeneratorBuilt-in generator for SARIF reports.
impl SarifMarkdownGenerator {
pub fn new(
markdown_format: MarkdownFormat,
with_emoji: bool
) -> Self;
}
pub enum MarkdownFormat {
CommonMark, // Standard Markdown
GitHubFlavored, // GitHub Flavored Markdown with HTML
}
pub enum Error {
JsonError(serde_json::Error),
GeneratorError(GeneratorError),
IoError(std::io::Error),
}
pub enum GeneratorError {
TemplateError(askama::Error),
}
pub enum BuilderError {
MissingContent,
}
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()?;
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
);
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),
}
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)
}
The library uses Askama for template rendering. Templates are located in templates/sarif/:
report.md - Main report templatemacros.md - Reusable template macrosTemplates 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>,
}
# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Test specific module
cargo test markdown::sarif
# Run benchmarks (requires nightly)
cargo +nightly bench
Generate and open the full API documentation:
cargo doc --no-deps --open
This crate requires Rust 1.90 or later.
serde - Serialization frameworkserde_json - JSON parsingserde-sarif - SARIF format supportaskama - Template enginechrono - Date/time handlingthiserror - Error handlingsarif-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
| 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 |
See the parent repository's CONTRIBUTING.md for guidelines.
See CHANGELOG.md for version history.
This project is dual-licensed under:
Choose the license that best suits your needs.