use std::time::{Duration, Instant};
use crate::instruments::*;
use crate::snapshot::{ItemKind, Snapshot};
use crate::util;
use crate::{HandlesObservations, Observation, PutsSnapshot};
pub struct Cockpit<L> {
name: Option<String>,
title: Option<String>,
description: Option<String>,
panels: Vec<Panel<L>>,
handlers: Vec<Box<dyn HandlesObservations<Label = L>>>,
snapshooters: Vec<Box<dyn PutsSnapshot>>,
last_activity_at: Instant,
max_inactivity_duration: Option<Duration>,
}
impl<L> Cockpit<L>
where
L: Clone + Eq + Send + 'static,
{
pub fn new<T: Into<String>>(name: T) -> Cockpit<L> {
let mut cockpit = Cockpit::default();
cockpit.name = Some(name.into());
cockpit
}
pub fn without_name() -> Cockpit<L> {
Cockpit::default()
}
#[deprecated(
since = "0.10.6",
note = "use get_name. this method will change its signature"
)]
pub fn name(&self) -> Option<&str> {
self.get_name()
}
pub fn get_name(&self) -> Option<&str> {
self.name.as_ref().map(|n| &**n)
}
pub fn set_name<T: Into<String>>(&mut self, name: T) {
self.name = Some(name.into())
}
pub fn set_title<T: Into<String>>(&mut self, title: T) {
self.title = Some(title.into())
}
pub fn set_description<T: Into<String>>(&mut self, description: T) {
self.description = Some(description.into())
}
pub fn set_inactivity_limit(&mut self, limit: Duration) {
self.max_inactivity_duration = Some(limit);
}
pub fn add_panel(&mut self, panel: Panel<L>) {
if let Some(name) = panel.name() {
if self
.panels
.iter()
.any(|p| p.name().map(|n| n == name).unwrap_or(false))
{
return;
}
}
self.panels.push(panel)
}
pub fn remove_panel<T: AsRef<str>>(&mut self, name: T) {
self.panels
.retain(|p| p.name().map(|n| n != name.as_ref()).unwrap_or(true))
}
pub fn panel(mut self, panel: Panel<L>) -> Self {
self.add_panel(panel);
self
}
pub fn get_panels(&self) -> Vec<&Panel<L>> {
self.panels.iter().collect()
}
pub fn get_panels_mut(&mut self) -> Vec<&mut Panel<L>> {
self.panels.iter_mut().collect()
}
pub fn add_handler<T>(&mut self, handler: T)
where
T: HandlesObservations<Label = L>,
{
self.handlers.push(Box::new(handler))
}
#[deprecated(
since = "0.10.6",
note = "use get_handlers. this method will change its signature"
)]
pub fn handlers(&self) -> Vec<&dyn HandlesObservations<Label = L>> {
self.get_handlers()
}
pub fn get_handlers(&self) -> Vec<&dyn HandlesObservations<Label = L>> {
self.handlers.iter().map(|h| &**h).collect()
}
pub fn handler<H: HandlesObservations<Label = L>>(mut self, handler: H) -> Self {
self.add_handler(handler);
self
}
pub fn add_snapshooter<T: PutsSnapshot>(&mut self, snapshooter: T) {
self.snapshooters.push(Box::new(snapshooter));
}
pub fn snapshooter<T: PutsSnapshot>(mut self, snapshooter: T) -> Self {
self.add_snapshooter(snapshooter);
self
}
#[deprecated(
since = "0.10.6",
note = "use get_snapshooters. this method will change its signature"
)]
pub fn snapshooters(&self) -> Vec<&dyn PutsSnapshot> {
self.get_snapshooters()
}
pub fn get_snapshooters(&self) -> Vec<&dyn PutsSnapshot> {
self.snapshooters.iter().map(|p| &**p).collect()
}
fn put_values_into_snapshot(&self, into: &mut Snapshot, descriptive: bool) {
util::put_default_descriptives(self, into, descriptive);
if let Some(d) = self.max_inactivity_duration {
if self.last_activity_at.elapsed() > d {
into.items
.push(("_inactive".to_string(), ItemKind::Boolean(true)));
into.items
.push(("_active".to_string(), ItemKind::Boolean(false)));
return;
} else {
into.items
.push(("_inactive".to_string(), ItemKind::Boolean(false)));
into.items
.push(("_active".to_string(), ItemKind::Boolean(true)));
}
};
self.panels
.iter()
.for_each(|p| p.put_snapshot(into, descriptive));
self.handlers
.iter()
.for_each(|h| h.put_snapshot(into, descriptive));
self.snapshooters
.iter()
.for_each(|s| s.put_snapshot(into, descriptive));
}
}
impl<L> PutsSnapshot for Cockpit<L>
where
L: Clone + Eq + Send + 'static,
{
fn put_snapshot(&self, into: &mut Snapshot, descriptive: bool) {
if let Some(ref name) = self.name {
let mut new_level = Snapshot::default();
self.put_values_into_snapshot(&mut new_level, descriptive);
into.items
.push((name.clone(), ItemKind::Snapshot(new_level)));
} else {
self.put_values_into_snapshot(into, descriptive);
}
}
}
impl<L> Default for Cockpit<L>
where
L: Clone + Eq + Send + 'static,
{
fn default() -> Cockpit<L> {
Cockpit {
name: None,
title: None,
description: None,
panels: Vec::new(),
handlers: Vec::new(),
snapshooters: Vec::new(),
last_activity_at: Instant::now(),
max_inactivity_duration: None,
}
}
}
impl<L> HandlesObservations for Cockpit<L>
where
L: Clone + Eq + Send + 'static,
{
type Label = L;
fn handle_observation(&mut self, observation: &Observation<Self::Label>) -> usize {
self.last_activity_at = Instant::now();
let mut instruments_updated = 0;
self.handlers
.iter_mut()
.for_each(|h| instruments_updated += h.handle_observation(&observation));
self.panels
.iter_mut()
.for_each(|p| instruments_updated += p.handle_observation(&observation));
instruments_updated
}
}
impl<L> crate::Descriptive for Cockpit<L> {
fn title(&self) -> Option<&str> {
self.title.as_ref().map(|n| &**n)
}
fn description(&self) -> Option<&str> {
self.description.as_ref().map(|n| &**n)
}
}