use crate::parser::{SarifValidator, ValidationResult};
use crate::types::location::ArtifactContent;
use crate::types::{
Address, ArtifactLocation, Location, LogicalLocation, Message, PhysicalLocation, Region,
};
#[derive(Debug, Clone)]
pub struct LocationBuilder {
id: Option<i32>,
physical_location: Option<PhysicalLocation>,
logical_locations: Vec<LogicalLocation>,
message: Option<Message>,
}
impl LocationBuilder {
pub fn new() -> Self {
Self {
id: None,
physical_location: None,
logical_locations: Vec::new(),
message: None,
}
}
pub fn with_id(mut self, id: i32) -> Self {
self.id = Some(id);
self
}
pub fn with_physical_location(mut self, physical_location: PhysicalLocation) -> Self {
self.physical_location = Some(physical_location);
self
}
pub fn with_physical_location_builder<F>(mut self, f: F) -> Self
where
F: FnOnce(PhysicalLocationBuilder) -> PhysicalLocationBuilder,
{
let builder = PhysicalLocationBuilder::new();
self.physical_location = Some(f(builder).build());
self
}
pub fn with_file_location(self, file_path: impl Into<String>) -> Self {
self.with_physical_location(PhysicalLocation::with_artifact_location(
ArtifactLocation::new(file_path),
))
}
pub fn with_file_region(
self,
file_path: impl Into<String>,
start_line: i32,
start_column: i32,
end_line: i32,
end_column: i32,
) -> Self {
self.with_physical_location(
PhysicalLocation::with_artifact_location(ArtifactLocation::new(file_path)).with_region(
Region::from_coordinates(start_line, start_column, end_line, end_column),
),
)
}
pub fn add_logical_location(mut self, logical_location: LogicalLocation) -> Self {
self.logical_locations.push(logical_location);
self
}
pub fn add_logical_locations(
mut self,
logical_locations: impl IntoIterator<Item = LogicalLocation>,
) -> Self {
self.logical_locations.extend(logical_locations);
self
}
pub fn with_message(mut self, message: Message) -> Self {
self.message = Some(message);
self
}
pub fn with_text_message(mut self, text: impl Into<String>) -> Self {
self.message = Some(Message::new(text));
self
}
pub fn validate(&self, validator: &SarifValidator) -> ValidationResult<()> {
if let Some(ref physical_location) = self.physical_location {
validator.validate_physical_location(physical_location)?;
}
for logical_location in &self.logical_locations {
validator.validate_logical_location(logical_location)?;
}
if let Some(ref message) = self.message {
validator.validate_message(message)?;
}
Ok(())
}
pub fn build(self) -> Location {
let mut location = Location::new();
location.id = self.id;
location.physical_location = self.physical_location;
location.message = self.message;
if !self.logical_locations.is_empty() {
location.logical_locations = Some(self.logical_locations);
}
location
}
pub fn build_validated(self, validator: &SarifValidator) -> ValidationResult<Location> {
self.validate(validator)?;
Ok(self.build())
}
}
impl Default for LocationBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct PhysicalLocationBuilder {
artifact_location: ArtifactLocation,
region: Option<Region>,
context_region: Option<Region>,
address: Option<Address>,
}
impl PhysicalLocationBuilder {
pub fn new() -> Self {
Self {
artifact_location: ArtifactLocation::new(""),
region: None,
context_region: None,
address: None,
}
}
pub fn with_artifact_location(artifact_location: ArtifactLocation) -> Self {
Self {
artifact_location,
region: None,
context_region: None,
address: None,
}
}
pub fn with_file_path(file_path: impl Into<String>) -> Self {
Self::with_artifact_location(ArtifactLocation::new(file_path))
}
pub fn set_artifact_location(mut self, artifact_location: ArtifactLocation) -> Self {
self.artifact_location = artifact_location;
self
}
pub fn set_file_path(mut self, file_path: impl Into<String>) -> Self {
self.artifact_location = ArtifactLocation::new(file_path);
self
}
pub fn with_region(mut self, region: Region) -> Self {
self.region = Some(region);
self
}
pub fn with_coordinates(
mut self,
start_line: i32,
start_column: i32,
end_line: i32,
end_column: i32,
) -> Self {
self.region = Some(Region::from_coordinates(
start_line,
start_column,
end_line,
end_column,
));
self
}
pub fn with_char_offset(mut self, char_offset: i32, char_length: i32) -> Self {
self.region = Some(Region::from_char_offset(char_offset, char_length));
self
}
pub fn with_line_column(mut self, line: i32, column: i32) -> Self {
self.region = Some(Region::from_coordinates(line, column, line, column));
self
}
pub fn with_region_snippet(
mut self,
start_line: i32,
start_column: i32,
end_line: i32,
end_column: i32,
snippet_text: impl Into<String>,
) -> Self {
let mut region = Region::from_coordinates(start_line, start_column, end_line, end_column);
region.snippet = Some(ArtifactContent {
text: Some(snippet_text.into()),
binary: None,
properties: None,
});
self.region = Some(region);
self
}
pub fn with_context_region(mut self, context_region: Region) -> Self {
self.context_region = Some(context_region);
self
}
pub fn with_address(mut self, address: Address) -> Self {
self.address = Some(address);
self
}
pub fn validate(&self, validator: &SarifValidator) -> ValidationResult<()> {
validator.validate_artifact_location(&self.artifact_location)?;
if let Some(ref region) = self.region {
validator.validate_region(region)?;
}
if let Some(ref context_region) = self.context_region {
validator.validate_region(context_region)?;
}
Ok(())
}
pub fn build(self) -> PhysicalLocation {
let mut physical_location =
PhysicalLocation::with_artifact_location(self.artifact_location);
physical_location.region = self.region;
physical_location.context_region = self.context_region;
physical_location.address = self.address;
physical_location
}
pub fn build_validated(self, validator: &SarifValidator) -> ValidationResult<PhysicalLocation> {
self.validate(validator)?;
Ok(self.build())
}
}
impl Default for PhysicalLocationBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct RegionBuilder {
start_line: Option<i32>,
start_column: Option<i32>,
end_line: Option<i32>,
end_column: Option<i32>,
char_offset: Option<i32>,
char_length: Option<i32>,
byte_offset: Option<i32>,
byte_length: Option<i32>,
snippet: Option<ArtifactContent>,
message: Option<Message>,
source_language: Option<String>,
}
impl RegionBuilder {
pub fn new() -> Self {
Self {
start_line: None,
start_column: None,
end_line: None,
end_column: None,
char_offset: None,
char_length: None,
byte_offset: None,
byte_length: None,
snippet: None,
message: None,
source_language: None,
}
}
pub fn with_coordinates(
mut self,
start_line: i32,
start_column: i32,
end_line: i32,
end_column: i32,
) -> Self {
self.start_line = Some(start_line);
self.start_column = Some(start_column);
self.end_line = Some(end_line);
self.end_column = Some(end_column);
self
}
pub fn with_char_offset(mut self, char_offset: i32, char_length: i32) -> Self {
self.char_offset = Some(char_offset);
self.char_length = Some(char_length);
self
}
pub fn with_byte_offset(mut self, byte_offset: i32, byte_length: i32) -> Self {
self.byte_offset = Some(byte_offset);
self.byte_length = Some(byte_length);
self
}
pub fn with_line_column(mut self, line: i32, column: i32) -> Self {
self.start_line = Some(line);
self.start_column = Some(column);
self.end_line = Some(line);
self.end_column = Some(column);
self
}
pub fn with_snippet_text(mut self, text: impl Into<String>) -> Self {
self.snippet = Some(ArtifactContent {
text: Some(text.into()),
binary: None,
properties: None,
});
self
}
pub fn with_snippet(mut self, snippet: ArtifactContent) -> Self {
self.snippet = Some(snippet);
self
}
pub fn with_message(mut self, message: Message) -> Self {
self.message = Some(message);
self
}
pub fn with_text_message(mut self, text: impl Into<String>) -> Self {
self.message = Some(Message::new(text));
self
}
pub fn with_source_language(mut self, language: impl Into<String>) -> Self {
self.source_language = Some(language.into());
self
}
pub fn validate(&self, validator: &SarifValidator) -> ValidationResult<()> {
let region = self.clone().build();
validator.validate_region(®ion)
}
pub fn build(self) -> Region {
Region {
start_line: self.start_line,
start_column: self.start_column,
end_line: self.end_line,
end_column: self.end_column,
char_offset: self.char_offset,
char_length: self.char_length,
byte_offset: self.byte_offset,
byte_length: self.byte_length,
snippet: self.snippet,
message: self.message,
source_language: self.source_language,
properties: None,
}
}
pub fn build_validated(self, validator: &SarifValidator) -> ValidationResult<Region> {
let region = self.build();
validator.validate_region(®ion)?;
Ok(region)
}
}
impl Default for RegionBuilder {
fn default() -> Self {
Self::new()
}
}