lcax_validation 3.4.3

LCAx is an open, machine and human-readable data format for exchanging LCA results.
Documentation
use crate::model::{Level, ValidationResult, ValidationSchema};
use crate::rules;
use lcax_models::assembly::AssemblyReference;
use lcax_models::epd::EPDReference;
use lcax_models::generic_impact_data::GenericDataReference;
use lcax_models::product::{ImpactData, ProductReference};
use lcax_models::project::Project as LCAxProject;
use log;
use valitron::available::{Message, Range, Required};
use valitron::*;

pub fn validate(
    project: &LCAxProject,
    validation_schema: &Vec<ValidationSchema>,
) -> Vec<ValidationResult> {
    let mut project_validator = Validator::new();
    let mut assembly_validator = Validator::new();
    let mut product_validator = Validator::new();
    let mut impact_validator = Validator::new();

    for schema in validation_schema {
        match schema.level {
            Level::Project => {
                project_validator = apply_rule(project_validator, schema);
            }
            Level::Assembly => {
                assembly_validator = apply_rule(assembly_validator, schema);
            }
            Level::Product => {
                product_validator = apply_rule(product_validator, schema);
            }
            Level::ImpactData => {
                impact_validator = apply_rule(impact_validator, schema);
            }
        }
    }

    let mut errors = vec![];

    match project_validator.validate(project) {
        Ok(()) => {
            log::debug!("Project validation successful - all checks passed");
        }
        Err(msg) => {
            log::info!("Validation failed for project");
            for (field_name, message) in msg {
                errors.push(ValidationResult {
                    field: field_name.as_str().to_string(),
                    message: message[0].to_string(),
                })
            }
        }
    }
    for assembly in &project.assemblies {
        match assembly {
            AssemblyReference::Assembly(_assembly) => {
                match assembly_validator.clone().validate(_assembly) {
                    Ok(()) => {
                        log::debug!(
                            "Validation successful for Assembly: {0} - all checks passed",
                            _assembly.id
                        );
                    }
                    Err(msg) => {
                        log::info!("Validation failed for Assembly: {0}", _assembly.id);
                        for (field_name, message) in msg {
                            errors.push(ValidationResult {
                                field: field_name.as_str().to_string(),
                                message: message[0].to_string(),
                            })
                        }
                    }
                }
                for product in &_assembly.products {
                    match product {
                        ProductReference::Product(_product) => {
                            match product_validator.clone().validate(_product) {
                                Ok(()) => {
                                    log::debug!(
                                        "Validation successful for Product: {0} - all checks passed",
                                        _product.id
                                    );
                                }
                                Err(msg) => {
                                    log::info!("Validation failed for Product: {0}", _product.id);
                                    for (field_name, message) in msg {
                                        errors.push(ValidationResult {
                                            field: field_name.as_str().to_string(),
                                            message: message[0].to_string(),
                                        })
                                    }
                                }
                            }
                            for impact in &_product.impact_data {
                                match impact {
                                    ImpactData::GenericData(GenericDataReference::GenericData(
                                        _data,
                                    )) => match impact_validator.clone().validate(_data) {
                                        Ok(()) => {
                                            log::debug!("Validation successful for ImpactData: {0} - all checks passed", _data.id);
                                        }
                                        Err(msg) => {
                                            log::info!(
                                                "Validation failed for ImpactData: {0}",
                                                _data.id
                                            );
                                            for (field_name, message) in msg {
                                                errors.push(ValidationResult {
                                                    field: field_name.as_str().to_string(),
                                                    message: message[0].to_string(),
                                                })
                                            }
                                        }
                                    },
                                    ImpactData::EPD(EPDReference::EPD(_data)) => {
                                        match impact_validator.clone().validate(_data) {
                                            Ok(()) => {
                                                log::debug!("Validation successful for EPD: {0} - all checks passed", _data.id);
                                            }
                                            Err(msg) => {
                                                log::info!(
                                                    "Validation failed for EPD: {0}",
                                                    _data.id
                                                );
                                                for (field_name, message) in msg {
                                                    errors.push(ValidationResult {
                                                        field: field_name.as_str().to_string(),
                                                        message: message[0].to_string(),
                                                    })
                                                }
                                            }
                                        }
                                    }
                                    _ => {}
                                }
                            }
                        }
                        ProductReference::Reference(_reference) => {}
                    }
                }
            }
            AssemblyReference::Reference(_reference) => {}
        }
    }

    if errors.is_empty() {
        log::info!("Validation successful - all checks passed");
    } else {
        log::info!("Validation failed");
    }
    errors
}

fn apply_rule<'a>(
    mut validator: Validator<'a, Message>,
    schema: &ValidationSchema,
) -> Validator<'a, Message> {
    if let Some(range) = &schema.rule.range {
        log::trace!(
            "At {:?} - Applying 'range' rule for: {:?} - {:?}",
            &schema.level,
            &schema.field,
            range
        );
        validator = validator.rule(&schema.field.as_str(), Range::new(range[0]..range[1]));
    }
    if let Some(required) = &schema.rule.required {
        log::trace!(
            "At {:?} - Applying 'required' rule for: {:?} - {:?}",
            &schema.level,
            &schema.field,
            required
        );
        validator = validator.rule(&schema.field.as_str(), Required);
    }
    if let Some(includes) = &schema.rule.includes {
        log::trace!(
            "At {:?} - Applying 'includes' rule for: {:?} - {:?}",
            &schema.level,
            &schema.field,
            includes
        );
        validator = validator.rule(&schema.field.as_str(), rules::Includes(includes.clone()));
    }
    if let Some(greater) = &schema.rule.greater {
        log::trace!(
            "At {:?} - Applying 'greater' rule for: {:?} - {:?}",
            &schema.level,
            &schema.field,
            greater
        );
        validator = validator.rule(&schema.field.as_str(), rules::Greater(*greater));
    }
    if let Some(less) = &schema.rule.less {
        log::trace!(
            "At {:?} - Applying 'less' rule for: {:?} - {:?}",
            &schema.level,
            &schema.field,
            less
        );
        validator = validator.rule(&schema.field.as_str(), rules::Less(*less));
    }
    if let Some(equal) = &schema.rule.equal {
        log::trace!(
            "At {:?} - Applying 'equal' rule for: {:?} - {:?}",
            &schema.level,
            &schema.field,
            equal
        );
        validator = validator.rule(&schema.field.as_str(), rules::Equal(equal.clone()));
    }
    if let Some(one_of) = &schema.rule.one_of {
        log::trace!(
            "At {:?} - Applying 'one of' rule for: {:?} - {:?}",
            &schema.level,
            &schema.field,
            one_of
        );
        validator = validator.rule(&schema.field.as_str(), rules::OneOf(one_of.clone()));
    }
    validator
}