framework-cqrs-lib 0.5.4

handle state-machine with data persist in journal and store mongo for restfull actix api
Documentation
use std::cmp::max;
use std::collections::HashMap;

use serde::Serialize;

use crate::cqrs::core::context::Context;
use crate::cqrs::core::data::Entity;
use crate::cqrs::core::repositories::query::Paged;
use crate::cqrs::models::jsonapi::{CanBeView, CanGetTypee, ManyView, PageView, PaginationView};
use crate::cqrs::models::views::{DataWrapperView, LinkView};
use crate::cqrs::models::views::entities::{EntityView, LinksEntityView};

pub trait CanBeManyView<T: Serialize + Clone> {
    fn to_many_view(
        &self,
        ctx: &Context,
        ontology: String,
        other_link: HashMap<String, String>,
    ) -> ManyView<T>;
}

impl<T: Serialize + Clone> CanBeManyView<T> for Paged<T> {
    fn to_many_view(&self, ctx: &Context, ontology: String, other_link: HashMap<String, String>) -> ManyView<T> {
        let external_url = ctx.meta
            .get("externalUrl")
            .map(|urlref| urlref.clone())
            .unwrap_or("unknown".to_string());

        let query_without_prefix = ctx.filters
            .iter()
            .map(|(k, v)| {
                format!("{k}={v}")
            })
            .collect::<Vec<String>>()
            .join("&");

        let query = if query_without_prefix.is_empty() {
            query_without_prefix
        } else {
            format!("?{query_without_prefix}")
        };


        let query_first_without_prefix = ctx.filters
            .iter()
            .filter(|(k, _)| **k != "page[number]".to_string() && **k != "page[size]".to_string())
            .map(|(k, v)| {
                format!("{k}={v}")
            })
            .chain(vec!["page[number]=0".to_string(), format!("page[size]={}", self.meta.page.size)])
            .collect::<Vec<String>>()
            .join("&");

        let query_first = if query_first_without_prefix.is_empty() {
            query_first_without_prefix
        } else {
            format!("?{query_first_without_prefix}")
        };

        let query_prev_without_prefix = ctx.filters
            .iter()
            .filter(|(k, _)| **k != "page[number]".to_string() && **k != "page[size]".to_string())
            .map(|(k, v)| {
                format!("{k}={v}")
            })
            .chain(vec![format!("page[number]={}", max(if self.meta.page.number > 0 { self.meta.page.number - 1 } else { 0 }, 0)), format!("page[size]={}", self.meta.page.size)])
            .collect::<Vec<String>>()
            .join("&");

        let query_prev = if query_prev_without_prefix.is_empty() {
            query_prev_without_prefix
        } else {
            format!("?{query_prev_without_prefix}")
        };


        let other_link_sanitize = other_link
            .into_iter()
            .map(|(k, v)| (k, format!("{external_url}/{v}")))
            .collect::<HashMap<String, String>>();


        let links = LinkView {
            links: HashMap::from([
                ("self".to_string(), format!("{external_url}/{ontology}")),
                ("last".to_string(), format!("{external_url}/{ontology}{}", query.clone())),
                ("prev".to_string(), format!("{external_url}/{ontology}{}", query_prev.clone())),
                ("first".to_string(), format!("{external_url}/{ontology}{query_first}")),
                ("next".to_string(), format!("{external_url}/{ontology}{}", query.clone())),
            ])
                .into_iter().chain(other_link_sanitize)
                .collect()
        };

        ManyView {
            data: self.data.clone(),
            meta: Some(
                PaginationView {
                    total_pages: self.meta.total_pages.clone(),
                    total_records: self.meta.total_records.clone(),
                    page: PageView {
                        number: self.meta.page.number.clone(),
                        size: self.meta.page.size.clone(),
                    },
                }
            ),
            links: Some(links),
        }
    }
}

pub fn from_states_to_entity_view<DATA, VIEW>(
    entity: Entity<DATA, String>,
    ontology: String,
    context: &Context,
) -> EntityView<VIEW>
where
    VIEW: Serialize + Clone,
    DATA: Clone + CanBeView<VIEW> + CanGetTypee,
{
    let entity_id = entity.entity_id.as_str();

    let external_url = context.meta
        .get("externalUrl")
        .map(|urlref| urlref.clone())
        .unwrap_or("unknown".to_string());

    let link_event = (
        "events".to_string(), format!("{external_url}/{ontology}/{entity_id}/events")
    );
    let link_self = (
        "self".to_string(), format!("{external_url}/{ontology}/{entity_id}")
    );

    EntityView {
        r#type: entity.data.get_type(),
        id: entity_id.to_string(),
        attributes: entity.data.to_view(),
        links: Some(LinksEntityView {
            links: HashMap::from([link_event, link_self])
        }),
    }
}

pub fn from_states_to_view<DATA, VIEW>(
    entity: Entity<DATA, String>,
    ontology: String,
    context: &Context,
) -> DataWrapperView<EntityView<VIEW>>
where
    VIEW: Serialize + Clone,
    DATA: Clone + CanBeView<VIEW> + CanGetTypee,
{
    DataWrapperView {
        data: from_states_to_entity_view(entity, ontology, context)
    }
}