soul-base 0.1.0

Data contract primitives for the Soul platform (IDs, Subject, Scope, Consent, Envelope, ...).
Documentation
use semver::Version;
use thiserror::Error;

use crate::{envelope::Envelope, scope::{Consent, Scope}, subject::Subject};

#[derive(Debug, Error)]
pub enum ValidateError {
    #[error("empty_field:{0}")]
    EmptyField(&'static str),
    #[error("invalid_semver:{0}")]
    InvalidSemVer(String),
    #[error("tenant_mismatch")]
    TenantMismatch,
}

pub trait Validate {
    fn validate(&self) -> Result<(), ValidateError>;
}

impl Validate for Subject {
    fn validate(&self) -> Result<(), ValidateError> {
        if self.actor_id.0.is_empty() {
            return Err(ValidateError::EmptyField("actor_id"));
        }
        if self.tenant.0.is_empty() {
            return Err(ValidateError::EmptyField("tenant"));
        }
        Ok(())
    }
}

impl Validate for Scope {
    fn validate(&self) -> Result<(), ValidateError> {
        if self.resource.is_empty() {
            return Err(ValidateError::EmptyField("resource"));
        }
        if self.action.is_empty() {
            return Err(ValidateError::EmptyField("action"));
        }
        Ok(())
    }
}

impl Validate for Consent {
    fn validate(&self) -> Result<(), ValidateError> {
        for scope in &self.scopes {
            scope.validate()?;
        }
        Ok(())
    }
}

impl<T> Validate for Envelope<T> {
    fn validate(&self) -> Result<(), ValidateError> {
        if self.envelope_id.0.is_empty() {
            return Err(ValidateError::EmptyField("envelope_id"));
        }
        if self.partition_key.is_empty() {
            return Err(ValidateError::EmptyField("partition_key"));
        }
        if Version::parse(&self.schema_ver).is_err() {
            return Err(ValidateError::InvalidSemVer(self.schema_ver.clone()));
        }
        self.actor.validate()?;
        if let Some(consent) = &self.consent {
            consent.validate()?;
        }
        if !self.partition_key.contains(&self.actor.tenant.0) {
            return Err(ValidateError::TenantMismatch);
        }
        Ok(())
    }
}