use crate::builder::ResultBuilder;
use crate::parser::{SarifValidator, ValidationResult};
use crate::types::run::ColumnKind;
use crate::types::{
Address, Artifact, ArtifactLocation, ExternalPropertyFileReferences, Invocation,
LogicalLocation, Result, Run, RunAutomationDetails, SpecialLocations, ThreadFlowLocation, Tool,
ToolComponent, WebRequest, WebResponse,
};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct RunBuilder {
tool: Tool,
results: Vec<Result>,
artifacts: Vec<Artifact>,
invocations: Vec<Invocation>,
logical_locations: Vec<LogicalLocation>,
original_uri_base_ids: Option<HashMap<String, ArtifactLocation>>,
automation_details: Option<RunAutomationDetails>,
column_kind: Option<ColumnKind>,
baseline_guid: Option<String>,
redaction_tokens: Vec<String>,
default_encoding: Option<String>,
default_source_language: Option<String>,
newline_sequences: Vec<String>,
external_property_file_references: Option<ExternalPropertyFileReferences>,
thread_flow_locations: Vec<ThreadFlowLocation>,
addresses: Vec<Address>,
web_requests: Vec<WebRequest>,
web_responses: Vec<WebResponse>,
special_locations: Option<SpecialLocations>,
}
impl RunBuilder {
pub fn new(tool: Tool) -> Self {
Self {
tool,
results: Vec::new(),
artifacts: Vec::new(),
invocations: Vec::new(),
logical_locations: Vec::new(),
original_uri_base_ids: None,
automation_details: None,
column_kind: None,
baseline_guid: None,
redaction_tokens: Vec::new(),
default_encoding: None,
default_source_language: None,
newline_sequences: Vec::new(),
external_property_file_references: None,
thread_flow_locations: Vec::new(),
addresses: Vec::new(),
web_requests: Vec::new(),
web_responses: Vec::new(),
special_locations: None,
}
}
pub fn with_tool(tool_name: impl Into<String>, version: Option<impl Into<String>>) -> Self {
let mut tool_component = ToolComponent::new(tool_name);
if let Some(ver) = version {
tool_component.version = Some(ver.into());
}
let tool = Tool {
driver: tool_component,
extensions: None,
properties: None,
};
Self::new(tool)
}
pub fn add_result(mut self, result: Result) -> Self {
self.results.push(result);
self
}
pub fn add_results(mut self, results: impl IntoIterator<Item = Result>) -> Self {
self.results.extend(results);
self
}
pub fn add_result_with<F>(mut self, f: F) -> Self
where
F: FnOnce(ResultBuilder) -> ResultBuilder,
{
let builder = ResultBuilder::with_text_message("");
let result = f(builder).build();
self.results.push(result);
self
}
pub fn add_artifact(mut self, artifact: Artifact) -> Self {
self.artifacts.push(artifact);
self
}
pub fn add_artifacts(mut self, artifacts: impl IntoIterator<Item = Artifact>) -> Self {
self.artifacts.extend(artifacts);
self
}
pub fn add_file_artifact(mut self, file_path: impl Into<String>) -> Self {
let artifact = Artifact::new(ArtifactLocation::new(file_path));
self.artifacts.push(artifact);
self
}
pub fn add_invocation(mut self, invocation: Invocation) -> Self {
self.invocations.push(invocation);
self
}
pub fn add_logical_location(mut self, logical_location: LogicalLocation) -> Self {
self.logical_locations.push(logical_location);
self
}
pub fn add_original_uri_base_id(
mut self,
key: impl Into<String>,
location: ArtifactLocation,
) -> Self {
if self.original_uri_base_ids.is_none() {
self.original_uri_base_ids = Some(HashMap::new());
}
self.original_uri_base_ids
.as_mut()
.unwrap()
.insert(key.into(), location);
self
}
pub fn with_automation_details(mut self, automation_details: RunAutomationDetails) -> Self {
self.automation_details = Some(automation_details);
self
}
pub fn with_column_kind(mut self, column_kind: ColumnKind) -> Self {
self.column_kind = Some(column_kind);
self
}
pub fn with_baseline_guid(mut self, baseline_guid: impl Into<String>) -> Self {
self.baseline_guid = Some(baseline_guid.into());
self
}
pub fn add_redaction_token(mut self, token: impl Into<String>) -> Self {
self.redaction_tokens.push(token.into());
self
}
pub fn with_default_encoding(mut self, encoding: impl Into<String>) -> Self {
self.default_encoding = Some(encoding.into());
self
}
pub fn with_default_source_language(mut self, language: impl Into<String>) -> Self {
self.default_source_language = Some(language.into());
self
}
pub fn add_newline_sequence(mut self, sequence: impl Into<String>) -> Self {
self.newline_sequences.push(sequence.into());
self
}
pub fn with_external_property_file_references(
mut self,
refs: ExternalPropertyFileReferences,
) -> Self {
self.external_property_file_references = Some(refs);
self
}
pub fn add_address(mut self, address: Address) -> Self {
self.addresses.push(address);
self
}
pub fn add_web_request(mut self, web_request: WebRequest) -> Self {
self.web_requests.push(web_request);
self
}
pub fn add_web_response(mut self, web_response: WebResponse) -> Self {
self.web_responses.push(web_response);
self
}
pub fn with_special_locations(mut self, special_locations: SpecialLocations) -> Self {
self.special_locations = Some(special_locations);
self
}
pub fn validate(&self, validator: &SarifValidator) -> ValidationResult<()> {
validator.validate_tool(&self.tool)?;
for result in &self.results {
validator.validate_result(result)?;
}
for artifact in &self.artifacts {
validator.validate_artifact(artifact)?;
}
for invocation in &self.invocations {
validator.validate_invocation(invocation)?;
}
for logical_location in &self.logical_locations {
validator.validate_logical_location(logical_location)?;
}
Ok(())
}
pub fn build(self) -> Run {
let mut run = Run::new(self.tool);
run.automation_details = self.automation_details;
run.baseline_guid = self.baseline_guid;
run.column_kind = self.column_kind;
run.default_encoding = self.default_encoding;
run.default_source_language = self.default_source_language;
run.external_property_file_references = self.external_property_file_references;
run.special_locations = self.special_locations;
if !self.results.is_empty() {
run.results = Some(self.results);
}
if !self.artifacts.is_empty() {
run.artifacts = Some(self.artifacts);
}
if !self.invocations.is_empty() {
run.invocations = Some(self.invocations);
}
if !self.logical_locations.is_empty() {
run.logical_locations = Some(self.logical_locations);
}
if let Some(uri_base_ids) = self.original_uri_base_ids
&& !uri_base_ids.is_empty()
{
run.original_uri_base_ids = Some(uri_base_ids);
}
if !self.redaction_tokens.is_empty() {
run.redaction_tokens = Some(self.redaction_tokens);
}
if !self.newline_sequences.is_empty() {
run.newline_sequences = Some(self.newline_sequences);
}
if !self.thread_flow_locations.is_empty() {
run.thread_flow_locations = Some(self.thread_flow_locations);
}
if !self.addresses.is_empty() {
run.addresses = Some(self.addresses);
}
if !self.web_requests.is_empty() {
run.web_requests = Some(self.web_requests);
}
if !self.web_responses.is_empty() {
run.web_responses = Some(self.web_responses);
}
run
}
pub fn build_validated(self, validator: &SarifValidator) -> ValidationResult<Run> {
self.validate(validator)?;
Ok(self.build())
}
}