use crate::validation::{
FailureReason, Validate, ValidationContext, ValidationError, ValidationPathComponent,
ValidationResult,
};
#[derive(Debug, PartialEq, Eq)]
pub struct Composition {
pub aggregate: AggregateType,
pub assemblies: Option<Vec<BomReference>>,
pub dependencies: Option<Vec<BomReference>>,
}
impl Validate for Composition {
fn validate_with_context(
&self,
context: ValidationContext,
) -> Result<ValidationResult, ValidationError> {
let mut results: Vec<ValidationResult> = vec![];
let aggregate_context =
context.extend_context_with_struct_field("Composition", "aggregate");
results.push(self.aggregate.validate_with_context(aggregate_context)?);
Ok(results
.into_iter()
.fold(ValidationResult::default(), |acc, result| acc.merge(result)))
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Compositions(pub Vec<Composition>);
impl Validate for Compositions {
fn validate_with_context(
&self,
context: ValidationContext,
) -> Result<ValidationResult, ValidationError> {
let mut results: Vec<ValidationResult> = vec![];
for (index, composition) in self.0.iter().enumerate() {
let composition_context =
context.extend_context(vec![ValidationPathComponent::Array { index }]);
results.push(composition.validate_with_context(composition_context)?);
}
Ok(results
.into_iter()
.fold(ValidationResult::default(), |acc, result| acc.merge(result)))
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum AggregateType {
Complete,
Incomplete,
IncompleteFirstPartyOnly,
IncompleteThirdPartyOnly,
Unknown,
NotSpecified,
#[doc(hidden)]
UnknownAggregateType(String),
}
impl ToString for AggregateType {
fn to_string(&self) -> String {
match self {
AggregateType::Complete => "complete",
AggregateType::Incomplete => "incomplete",
AggregateType::IncompleteFirstPartyOnly => "incomplete_first_party_only",
AggregateType::IncompleteThirdPartyOnly => "incomplete_third_party_only",
AggregateType::Unknown => "unknown",
AggregateType::NotSpecified => "not_specified",
AggregateType::UnknownAggregateType(uat) => uat,
}
.to_string()
}
}
impl AggregateType {
pub(crate) fn new_unchecked<A: AsRef<str>>(value: A) -> Self {
match value.as_ref() {
"complete" => Self::Complete,
"incomplete" => Self::Incomplete,
"incomplete_first_party_only" => Self::IncompleteFirstPartyOnly,
"incomplete_third_party_only" => Self::IncompleteThirdPartyOnly,
"unknown" => Self::Unknown,
"not_specified" => Self::NotSpecified,
unknown => Self::UnknownAggregateType(unknown.to_string()),
}
}
}
impl Validate for AggregateType {
fn validate_with_context(
&self,
context: ValidationContext,
) -> Result<ValidationResult, ValidationError> {
match self {
AggregateType::UnknownAggregateType(_) => Ok(ValidationResult::Failed {
reasons: vec![FailureReason {
message: "Unknown aggregate type".to_string(),
context,
}],
}),
_ => Ok(ValidationResult::Passed),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct BomReference(pub(crate) String);
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn it_should_pass_validation() {
let validation_result = Compositions(vec![Composition {
aggregate: AggregateType::Complete,
assemblies: Some(vec![BomReference("reference".to_string())]),
dependencies: Some(vec![BomReference("reference".to_string())]),
}])
.validate()
.expect("Error while validating");
assert_eq!(validation_result, ValidationResult::Passed);
}
#[test]
fn it_should_fail_validation() {
let validation_result = Compositions(vec![Composition {
aggregate: AggregateType::UnknownAggregateType("unknown aggregate type".to_string()),
assemblies: Some(vec![BomReference("reference".to_string())]),
dependencies: Some(vec![BomReference("reference".to_string())]),
}])
.validate()
.expect("Error while validating");
assert_eq!(
validation_result,
ValidationResult::Failed {
reasons: vec![FailureReason {
message: "Unknown aggregate type".to_string(),
context: ValidationContext(vec![
ValidationPathComponent::Array { index: 0 },
ValidationPathComponent::Struct {
struct_name: "Composition".to_string(),
field_name: "aggregate".to_string()
}
])
}]
}
);
}
}