use crate::parser::{SarifValidator, ValidationResult};
use crate::types::{
ArtifactLocation, Attachment, BaselineState, CodeFlow, Fix, GraphTraversal, Kind, Level,
Location, Message, PhysicalLocation, Region, Result, ResultProvenance, Stack, Suppression,
};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ResultBuilder {
message: Message,
rule_id: Option<String>,
rule_index: Option<i32>,
level: Option<Level>,
kind: Option<Kind>,
locations: Vec<Location>,
code_flows: Vec<CodeFlow>,
stacks: Vec<Stack>,
fixes: Vec<Fix>,
suppressions: Vec<Suppression>,
baseline_state: Option<BaselineState>,
rank: Option<f64>,
guid: Option<String>,
correlation_guid: Option<String>,
occurrence_count: Option<i32>,
partial_fingerprints: Option<HashMap<String, String>>,
fingerprints: Option<HashMap<String, String>>,
graph_traversals: Vec<GraphTraversal>,
attachments: Vec<Attachment>,
hosted_viewer_uri: Option<String>,
work_item_uris: Vec<String>,
provenance: Option<ResultProvenance>,
}
impl ResultBuilder {
pub fn new(message: impl Into<Message>) -> Self {
Self {
message: message.into(),
rule_id: None,
rule_index: None,
level: None,
kind: None,
locations: Vec::new(),
code_flows: Vec::new(),
stacks: Vec::new(),
fixes: Vec::new(),
suppressions: Vec::new(),
baseline_state: None,
rank: None,
guid: None,
correlation_guid: None,
occurrence_count: None,
partial_fingerprints: None,
fingerprints: None,
graph_traversals: Vec::new(),
attachments: Vec::new(),
hosted_viewer_uri: None,
work_item_uris: Vec::new(),
provenance: None,
}
}
pub fn with_text_message(text: impl Into<String>) -> Self {
Self::new(Message::new(text))
}
pub fn with_rule_id(mut self, rule_id: impl Into<String>) -> Self {
self.rule_id = Some(rule_id.into());
self
}
pub fn with_rule_index(mut self, rule_index: i32) -> Self {
self.rule_index = Some(rule_index);
self
}
pub fn with_level(mut self, level: Level) -> Self {
self.level = Some(level);
self
}
pub fn with_kind(mut self, kind: Kind) -> Self {
self.kind = Some(kind);
self
}
pub fn add_location(mut self, location: Location) -> Self {
self.locations.push(location);
self
}
pub fn add_locations(mut self, locations: impl IntoIterator<Item = Location>) -> Self {
self.locations.extend(locations);
self
}
pub fn add_file_location(
self,
file_path: impl Into<String>,
start_line: i32,
start_column: i32,
) -> Self {
let location = Location::with_physical_location(
PhysicalLocation::with_artifact_location(ArtifactLocation::new(file_path)).with_region(
Region::from_coordinates(start_line, start_column, start_line, start_column + 10),
),
);
self.add_location(location)
}
pub fn add_file_region(
self,
file_path: impl Into<String>,
start_line: i32,
start_column: i32,
end_line: i32,
end_column: i32,
) -> Self {
let location = Location::with_physical_location(
PhysicalLocation::with_artifact_location(ArtifactLocation::new(file_path)).with_region(
Region::from_coordinates(start_line, start_column, end_line, end_column),
),
);
self.add_location(location)
}
pub fn add_file_location_with_region(
self,
file_path: impl Into<String>,
region: Region,
) -> Self {
let location = Location::with_physical_location(
PhysicalLocation::with_artifact_location(ArtifactLocation::new(file_path)).with_region(region),
);
self.add_location(location)
}
pub fn add_code_flow(mut self, code_flow: CodeFlow) -> Self {
self.code_flows.push(code_flow);
self
}
pub fn add_stack(mut self, stack: Stack) -> Self {
self.stacks.push(stack);
self
}
pub fn add_fix(mut self, fix: Fix) -> Self {
self.fixes.push(fix);
self
}
pub fn add_suppression(mut self, suppression: Suppression) -> Self {
self.suppressions.push(suppression);
self
}
pub fn with_baseline_state(mut self, baseline_state: BaselineState) -> Self {
self.baseline_state = Some(baseline_state);
self
}
pub fn with_rank(mut self, rank: f64) -> Self {
self.rank = Some(rank);
self
}
pub fn with_guid(mut self, guid: impl Into<String>) -> Self {
self.guid = Some(guid.into());
self
}
pub fn with_correlation_guid(mut self, correlation_guid: impl Into<String>) -> Self {
self.correlation_guid = Some(correlation_guid.into());
self
}
pub fn with_occurrence_count(mut self, count: i32) -> Self {
self.occurrence_count = Some(count);
self
}
pub fn add_partial_fingerprint(
mut self,
key: impl Into<String>,
value: impl Into<String>,
) -> Self {
if self.partial_fingerprints.is_none() {
self.partial_fingerprints = Some(HashMap::new());
}
self.partial_fingerprints
.as_mut()
.unwrap()
.insert(key.into(), value.into());
self
}
pub fn add_fingerprint(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
if self.fingerprints.is_none() {
self.fingerprints = Some(HashMap::new());
}
self.fingerprints
.as_mut()
.unwrap()
.insert(key.into(), value.into());
self
}
pub fn add_work_item_uri(mut self, uri: impl Into<String>) -> Self {
self.work_item_uris.push(uri.into());
self
}
pub fn with_hosted_viewer_uri(mut self, uri: impl Into<String>) -> Self {
self.hosted_viewer_uri = Some(uri.into());
self
}
pub fn with_provenance(mut self, provenance: ResultProvenance) -> Self {
self.provenance = Some(provenance);
self
}
pub fn validate(&self, validator: &SarifValidator) -> ValidationResult<()> {
validator.validate_message(&self.message)?;
for location in &self.locations {
validator.validate_location(location)?;
}
if let Some(ref uri) = self.hosted_viewer_uri {
validator.validate_uri(uri)?;
}
for uri in &self.work_item_uris {
validator.validate_uri(uri)?;
}
Ok(())
}
pub fn build(self) -> Result {
let mut result = Result::new(self.message);
result.rule_id = self.rule_id;
result.rule_index = self.rule_index;
result.level = self.level;
result.kind = self.kind;
result.baseline_state = self.baseline_state;
result.rank = self.rank;
result.guid = self.guid;
result.correlation_guid = self.correlation_guid;
result.occurrence_count = self.occurrence_count;
result.partial_fingerprints = self.partial_fingerprints;
result.fingerprints = self.fingerprints;
result.hosted_viewer_uri = self.hosted_viewer_uri;
result.provenance = self.provenance;
if !self.locations.is_empty() {
result.locations = Some(self.locations);
}
if !self.code_flows.is_empty() {
result.code_flows = Some(self.code_flows);
}
if !self.stacks.is_empty() {
result.stacks = Some(self.stacks);
}
if !self.fixes.is_empty() {
result.fixes = Some(self.fixes);
}
if !self.suppressions.is_empty() {
result.suppressions = Some(self.suppressions);
}
if !self.graph_traversals.is_empty() {
result.graph_traversals = Some(self.graph_traversals);
}
if !self.attachments.is_empty() {
result.attachments = Some(self.attachments);
}
if !self.work_item_uris.is_empty() {
result.work_item_uris = Some(self.work_item_uris);
}
result
}
pub fn build_validated(self, validator: &SarifValidator) -> ValidationResult<Result> {
self.validate(validator)?;
Ok(self.build())
}
}