kmp-application 0.1.8

Application services of the KMP kernel: the use cases behind ingest, wake, ask, near, rewind and trace
Documentation
use std::collections::BTreeSet;

use crate::queries::QueryApplicationService;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScopeValidation {
    pub allowed: bool,
    pub required_scopes: Vec<String>,
    pub provided_scopes: Vec<String>,
    pub missing_scopes: Vec<String>,
    pub extra_scopes: Vec<String>,
    pub reason: String,
    pub diagnostics: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ValidateScopeQuery {
    pub required_scopes: Vec<String>,
    pub provided_scopes: Vec<String>,
}

#[derive(Debug)]
pub struct ValidateScopeUseCase;

impl ValidateScopeUseCase {
    pub fn execute(required_scopes: &[String], provided_scopes: &[String]) -> ScopeValidation {
        let required = dedupe_scopes(required_scopes);
        let provided = dedupe_scopes(provided_scopes);

        let required_set: BTreeSet<_> = required.iter().cloned().collect();
        let provided_set: BTreeSet<_> = provided.iter().cloned().collect();

        let missing_scopes = required_set
            .difference(&provided_set)
            .cloned()
            .collect::<Vec<_>>();
        let extra_scopes = provided_set
            .difference(&required_set)
            .cloned()
            .collect::<Vec<_>>();
        let allowed = missing_scopes.is_empty() && extra_scopes.is_empty();
        let reason = if allowed {
            "scope validation passed".to_string()
        } else {
            "scope validation failed".to_string()
        };

        ScopeValidation {
            allowed,
            required_scopes: required,
            provided_scopes: provided,
            missing_scopes,
            extra_scopes,
            reason,
            diagnostics: Vec::new(),
        }
    }
}

impl<G, D, S> QueryApplicationService<G, D, S> {
    pub fn validate_scope(&self, query: ValidateScopeQuery) -> ScopeValidation {
        ValidateScopeUseCase::execute(&query.required_scopes, &query.provided_scopes)
    }
}

pub fn dedupe_scopes(scopes: &[String]) -> Vec<String> {
    scopes
        .iter()
        .map(|scope| scope.trim())
        .filter(|scope| !scope.is_empty())
        .map(ToOwned::to_owned)
        .collect::<BTreeSet<_>>()
        .into_iter()
        .collect()
}