use crate::parser::{SarifError, SarifResult};
use crate::types::SarifLog;
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Write};
use std::path::Path;
pub fn parse_sarif_from_str(json: &str) -> SarifResult<SarifLog> {
let sarif: SarifLog = serde_json::from_str(json)?;
Ok(sarif)
}
pub fn parse_sarif_from_reader<R: Read>(reader: R) -> SarifResult<SarifLog> {
let sarif: SarifLog = serde_json::from_reader(reader)?;
Ok(sarif)
}
pub fn parse_sarif_from_file<P: AsRef<Path>>(path: P) -> SarifResult<SarifLog> {
let file = File::open(path)?;
let reader = BufReader::new(file);
parse_sarif_from_reader(reader)
}
pub fn serialize_sarif_to_string(sarif: &SarifLog) -> SarifResult<String> {
let json = serde_json::to_string(sarif)?;
Ok(json)
}
pub fn serialize_sarif_to_string_pretty(sarif: &SarifLog) -> SarifResult<String> {
let json = serde_json::to_string_pretty(sarif)?;
Ok(json)
}
pub fn serialize_sarif_to_writer<W: Write>(writer: W, sarif: &SarifLog) -> SarifResult<()> {
serde_json::to_writer(writer, sarif)?;
Ok(())
}
pub fn serialize_sarif_to_writer_pretty<W: Write>(writer: W, sarif: &SarifLog) -> SarifResult<()> {
serde_json::to_writer_pretty(writer, sarif)?;
Ok(())
}
pub fn serialize_sarif_to_file<P: AsRef<Path>>(path: P, sarif: &SarifLog) -> SarifResult<()> {
let file = File::create(path)?;
let writer = BufWriter::new(file);
serialize_sarif_to_writer_pretty(writer, sarif)
}
pub fn parse_json_value<T>(value: &serde_json::Value) -> SarifResult<T>
where
T: serde::de::DeserializeOwned,
{
let result: T = serde_json::from_value(value.clone())?;
Ok(result)
}
pub fn to_json_value<T>(item: &T) -> SarifResult<serde_json::Value>
where
T: serde::Serialize,
{
let value = serde_json::to_value(item)?;
Ok(value)
}
pub fn minify_json(json: &str) -> SarifResult<String> {
let value: serde_json::Value = serde_json::from_str(json)?;
let minified = serde_json::to_string(&value)?;
Ok(minified)
}
pub fn prettify_json(json: &str) -> SarifResult<String> {
let value: serde_json::Value = serde_json::from_str(json)?;
let prettified = serde_json::to_string_pretty(&value)?;
Ok(prettified)
}
pub fn validate_json_structure(json: &str) -> SarifResult<()> {
let _: serde_json::Value = serde_json::from_str(json)?;
Ok(())
}
pub struct SarifParser {
pub validate: bool,
pub max_size: Option<usize>,
pub allow_unknown_fields: bool,
}
impl Default for SarifParser {
fn default() -> Self {
Self {
validate: true,
max_size: None,
allow_unknown_fields: true,
}
}
}
impl SarifParser {
pub fn new() -> Self {
Self::default()
}
pub fn with_validation(mut self, validate: bool) -> Self {
self.validate = validate;
self
}
pub fn with_max_size(mut self, max_size: usize) -> Self {
self.max_size = Some(max_size);
self
}
pub fn with_allow_unknown_fields(mut self, allow: bool) -> Self {
self.allow_unknown_fields = allow;
self
}
pub fn parse_from_str(&self, json: &str) -> SarifResult<SarifLog> {
if let Some(max_size) = self.max_size
&& json.len() > max_size
{
return Err(SarifError::custom(format!(
"JSON size {} exceeds maximum {}",
json.len(),
max_size
)));
}
let sarif = if self.allow_unknown_fields {
serde_json::from_str(json)?
} else {
serde_json::from_str(json)?
};
if self.validate {
}
Ok(sarif)
}
pub fn parse_from_file<P: AsRef<Path>>(&self, path: P) -> SarifResult<SarifLog> {
let content = std::fs::read_to_string(path)?;
self.parse_from_str(&content)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::*;
#[test]
fn test_minimal_sarif_serialization() {
let sarif = SarifLog::v2_1_0();
let json = serialize_sarif_to_string(&sarif).unwrap();
assert!(json.contains("\"version\":\"2.1.0\""));
assert!(json.contains("\"runs\":[]"));
}
#[test]
fn test_sarif_round_trip() {
let original = SarifLog::v2_1_0();
let json = serialize_sarif_to_string(&original).unwrap();
let parsed = parse_sarif_from_str(&json).unwrap();
assert_eq!(original.schema, parsed.schema);
assert_eq!(original.version, parsed.version);
assert_eq!(original.runs, parsed.runs);
assert_eq!(
original.inline_external_properties,
parsed.inline_external_properties
);
match (&original.properties, &parsed.properties) {
(None, None) => {}
(None, Some(map)) if map.is_empty() => {}
(Some(map), None) if map.is_empty() => {}
(Some(orig), Some(parsed)) => assert_eq!(orig, parsed),
_ => panic!(
"Properties don't match: original={:?}, parsed={:?}",
original.properties, parsed.properties
),
}
}
#[test]
fn test_parser_configuration() {
let parser = SarifParser::new()
.with_validation(false)
.with_max_size(1024);
assert!(!parser.validate);
assert_eq!(parser.max_size, Some(1024));
}
#[test]
fn test_max_size_enforcement() {
let parser = SarifParser::new().with_max_size(10);
let large_json = "a".repeat(20);
let result = parser.parse_from_str(&large_json);
assert!(result.is_err());
}
}