use super::{Actor, ScopeId};
use ptree::TreeItem;
use serde::{Deserialize, Serialize};
use std::time::SystemTime;
#[repr(u8)]
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub enum ServiceStatus {
Starting = 0,
Initializing = 1,
Degraded = 2,
Running = 3,
Stopping = 4,
Maintenance = 5,
Stopped = 6,
Idle = 7,
Outage = 8,
}
impl std::fmt::Display for ServiceStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
ServiceStatus::Starting => "Starting",
ServiceStatus::Initializing => "Initializing",
ServiceStatus::Degraded => "Degraded",
ServiceStatus::Running => "Running",
ServiceStatus::Idle => "Idle",
ServiceStatus::Outage => "Outage",
ServiceStatus::Stopping => "Stopping",
ServiceStatus::Maintenance => "Maintenance",
ServiceStatus::Stopped => "Stopped",
}
)
}
}
impl Default for ServiceStatus {
fn default() -> Self {
Self::Starting
}
}
#[derive(Clone, Debug, Serialize)]
pub struct Service {
#[serde(skip_serializing)]
pub actor_type_id: std::any::TypeId,
pub actor_type_name: &'static str,
pub status: ServiceStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub directory: Option<String>,
pub up_since: SystemTime,
pub downtime_ms: u64,
#[serde(skip_serializing_if = "std::collections::HashMap::is_empty")]
pub microservices: std::collections::HashMap<ScopeId, Self>,
#[serde(skip_serializing_if = "std::collections::HashMap::is_empty")]
pub inactive: std::collections::HashMap<String, ScopeId>,
}
pub struct ServiceScopesIterator<'a> {
actor_type_id: std::any::TypeId,
inner: std::collections::hash_map::Iter<'a, ScopeId, Service>,
}
impl<'a> ServiceScopesIterator<'a> {
fn new<T: 'static>(iter: std::collections::hash_map::Iter<'a, ScopeId, Service>) -> Self {
Self {
actor_type_id: std::any::TypeId::of::<T>(),
inner: iter,
}
}
}
impl<'a> Iterator for ServiceScopesIterator<'a> {
type Item = ScopeId;
fn next(&mut self) -> Option<Self::Item> {
while let Some((scope_id, service)) = self.inner.next() {
if service.actor_type_id() == self.actor_type_id {
return Some(*scope_id);
} else {
continue;
}
}
None
}
}
impl Service {
pub fn new<A: 'static + Actor<S>, S: SupHandle<A>>(directory: Option<String>) -> Self {
Self {
actor_type_id: std::any::TypeId::of::<A>(),
actor_type_name: A::type_name(),
directory: directory.into(),
status: ServiceStatus::Starting,
up_since: SystemTime::now(),
downtime_ms: 0,
microservices: std::collections::HashMap::new(),
inactive: std::collections::HashMap::new(),
}
}
pub fn scopes_iter<'a, Child: 'static>(&'a self) -> ServiceScopesIterator<'a> {
ServiceScopesIterator::new::<Child>(self.microservices.iter())
}
pub fn actor_type_id(&self) -> std::any::TypeId {
self.actor_type_id
}
pub fn actor_type_name(&self) -> &'static str {
self.actor_type_name
}
pub fn with_status(mut self, service_status: ServiceStatus) -> Self {
self.status = service_status;
self
}
pub fn update_status(&mut self, service_status: ServiceStatus) {
self.status = service_status;
}
pub fn directory(&self) -> &Option<String> {
&self.directory
}
pub fn with_downtime_ms(mut self, downtime_ms: u64) -> Self {
self.downtime_ms = downtime_ms;
self
}
pub fn is_type<T: 'static>(&self) -> bool {
let is_type_id = std::any::TypeId::of::<T>();
self.actor_type_id == is_type_id
}
pub fn is_stopping(&self) -> bool {
self.status == ServiceStatus::Stopping
}
pub fn is_stopped(&self) -> bool {
self.status == ServiceStatus::Stopped
}
pub fn is_running(&self) -> bool {
self.status == ServiceStatus::Running
}
pub fn is_idle(&self) -> bool {
self.status == ServiceStatus::Idle
}
pub fn is_outage(&self) -> bool {
self.status == ServiceStatus::Outage
}
pub fn is_starting(&self) -> bool {
self.status == ServiceStatus::Starting
}
pub fn is_initializing(&self) -> bool {
self.status == ServiceStatus::Initializing
}
pub fn is_maintenance(&self) -> bool {
self.status == ServiceStatus::Maintenance
}
pub fn is_degraded(&self) -> bool {
self.status == ServiceStatus::Degraded
}
pub fn status(&self) -> &ServiceStatus {
&self.status
}
pub fn microservices(&self) -> &std::collections::HashMap<ScopeId, Service> {
&self.microservices
}
#[allow(unused)]
pub(crate) fn microservices_mut(&mut self) -> &mut std::collections::HashMap<ScopeId, Service> {
&mut self.microservices
}
#[allow(unused)]
pub(crate) fn inactive_mut(&mut self) -> &mut std::collections::HashMap<String, ScopeId> {
&mut self.inactive
}
}
impl TreeItem for Service {
type Child = Service;
fn write_self<W: std::io::Write>(&self, f: &mut W, _style: &ptree::Style) -> std::io::Result<()> {
write!(
f,
"actor: {}, dir: {:?}, status: {}, uptime: {}",
self.actor_type_name,
self.directory,
self.status,
self.up_since.elapsed().expect("Expected elapsed to unwrap").as_millis()
)
}
fn children(&self) -> std::borrow::Cow<[Self::Child]> {
self.microservices
.clone()
.into_iter()
.map(|(_, c)| c)
.collect::<Vec<Self::Child>>()
.into()
}
}
impl std::fmt::Display for Service {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut buf = std::io::Cursor::new(Vec::<u8>::new());
ptree::write_tree(self, &mut buf).ok();
write!(f, "{}", String::from_utf8_lossy(&buf.into_inner()))
}
}
#[async_trait::async_trait]
pub trait SupHandle<T: Send>: 'static + Send + Sized + Sync {
type Event;
async fn report(&self, scope_id: super::ScopeId, service: Service) -> Option<()>
where
T: Actor<Self>,
Self: SupHandle<T>;
async fn eol(self, scope_id: super::ScopeId, service: Service, actor: T, r: super::ActorResult<()>) -> Option<()>
where
T: Actor<Self>;
}
pub trait ServiceEvent<T>: Send + 'static {
fn report_event(scope: super::ScopeId, service: Service) -> Self;
fn eol_event(scope: super::ScopeId, service: Service, actor: T, r: super::ActorResult<()>) -> Self;
}