use std::collections::HashMap;
use diesel::prelude::*;
use crate::admin::store::{
diesel::{
models::{ServiceArgumentModel, ServiceModel},
schema::{service, service_argument},
},
error::AdminServiceStoreError,
Service, ServiceBuilder,
};
use super::AdminServiceStoreOperations;
pub(in crate::admin::store::diesel) trait AdminServiceStoreListServicesOperation {
fn list_services(
&self,
circuit_id: &str,
) -> Result<Box<dyn ExactSizeIterator<Item = Service>>, AdminServiceStoreError>;
}
impl<'a, C> AdminServiceStoreListServicesOperation for AdminServiceStoreOperations<'a, C>
where
C: diesel::Connection,
String: diesel::deserialize::FromSql<diesel::sql_types::Text, C::Backend>,
i64: diesel::deserialize::FromSql<diesel::sql_types::BigInt, C::Backend>,
i32: diesel::deserialize::FromSql<diesel::sql_types::Integer, C::Backend>,
{
fn list_services(
&self,
circuit_id: &str,
) -> Result<Box<dyn ExactSizeIterator<Item = Service>>, AdminServiceStoreError> {
let mut services: HashMap<String, ServiceModel> = HashMap::new();
let mut arguments_map: HashMap<String, Vec<ServiceArgumentModel>> = HashMap::new();
for (service, opt_arg) in service::table
.filter(service::circuit_id.eq(&circuit_id))
.left_join(
service_argument::table.on(service::circuit_id
.eq(service_argument::circuit_id)
.and(service::service_id.eq(service_argument::service_id))),
)
.select((
service::all_columns,
service_argument::all_columns.nullable(),
))
.load::<(ServiceModel, Option<ServiceArgumentModel>)>(self.conn)?
{
if let Some(arg_model) = opt_arg {
if let Some(args) = arguments_map.get_mut(&service.service_id) {
args.push(arg_model);
} else {
arguments_map.insert(service.service_id.to_string(), vec![arg_model]);
}
}
if !services.contains_key(&service.service_id) {
services.insert(service.service_id.to_string(), service);
}
}
let mut service_vec: Vec<ServiceModel> =
services.into_iter().map(|(_, service)| service).collect();
service_vec.sort_by_key(|service| service.position);
let ret_services: Vec<Service> = service_vec
.into_iter()
.map(|service| {
let mut builder = ServiceBuilder::new()
.with_service_id(&service.service_id)
.with_service_type(&service.service_type)
.with_node_id(&service.node_id);
if let Some(args) = arguments_map.get_mut(&service.service_id) {
args.sort_by_key(|arg| arg.position);
builder = builder.with_arguments(
&args
.iter()
.map(|args| (args.key.to_string(), args.value.to_string()))
.collect::<Vec<(String, String)>>(),
);
}
builder
.build()
.map_err(AdminServiceStoreError::InvalidStateError)
})
.collect::<Result<Vec<Service>, AdminServiceStoreError>>()?;
Ok(Box::new(ret_services.into_iter()))
}
}