use std::fmt;
use std::time::Duration;
use json::{stringify, stringify_pretty, JsonValue};
use crate::observation::ObservedValue;
#[derive(Debug, Clone, PartialEq)]
pub struct Snapshot {
pub items: Vec<(String, ItemKind)>,
}
impl Default for Snapshot {
fn default() -> Snapshot {
Snapshot { items: Vec::new() }
}
}
impl Snapshot {
pub fn push<K: Into<String>, V: Into<ItemKind>>(&mut self, k: K, v: V) {
self.items.push((k.into(), v.into()))
}
pub fn find_with_separator(&self, path: &str, separator: char) -> FindItem {
let path: Vec<&str> = path.split(separator).filter(|x| !x.is_empty()).collect();
if path.is_empty() {
return FindItem::NotFound;
}
if let Some(item) = self
.items
.iter()
.find(|&&(ref name, _)| name == path[0])
.map(|x| &x.1)
{
find_item(item, &path[1..])
} else {
FindItem::NotFound
}
}
pub fn find(&self, path: &str) -> FindItem {
self.find_with_separator(path, '/')
}
pub fn to_default_json(&self) -> String {
self.to_json_internal(&JsonConfig::default())
}
pub fn to_json(&self, config: &JsonConfig) -> String {
self.to_json_internal(config)
}
fn to_json_internal(&self, config: &JsonConfig) -> String {
let data = self.to_json_value(config);
if let Some(indent) = config.pretty {
stringify_pretty(data, indent)
} else {
stringify(data)
}
}
fn to_json_value(&self, config: &JsonConfig) -> JsonValue {
let mut data = JsonValue::new_object();
self.items
.iter()
.for_each(|&(ref k, ref v)| data[k] = v.to_json_value(config));
data
}
}
pub fn find_item<'a, T>(item: &'a ItemKind, path: &[T]) -> FindItem<'a>
where
T: AsRef<str>,
{
if path.is_empty() {
return FindItem::Found(item);
};
if path[0].as_ref().is_empty() {
return find_item(item, &path[1..]);
}
match *item {
ItemKind::Snapshot(ref snapshot) => {
if let Some(item) = snapshot
.items
.iter()
.find(|&&(ref name, _)| name == path[0].as_ref())
.map(|x| &x.1)
{
find_item(item, &path[1..])
} else {
FindItem::NotFound
}
}
ref other => FindItem::Found(other),
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum FindItem<'a> {
Found(&'a ItemKind),
NotFound,
}
impl<'a> FindItem<'a> {
pub fn opt(&self) -> Option<&ItemKind> {
use self::FindItem::*;
match self {
Found(ref v) => Some(v),
NotFound => None,
}
}
pub fn find_with_separator(&self, path: &str, separator: char) -> FindItem {
match self {
FindItem::Found(ItemKind::Snapshot(snapshot)) => {
snapshot.find_with_separator(path, separator)
}
_ => FindItem::NotFound,
}
}
pub fn find(&self, path: &str) -> FindItem {
self.find_with_separator(path, '/')
}
pub fn to_duration_nanoseconds(&self) -> FindDurationItem {
match self {
FindItem::Found(ref v) => match v {
ItemKind::UInt(v) => FindDurationItem::Duration(Duration::from_nanos(*v)),
ItemKind::Int(v) => FindDurationItem::Duration(Duration::from_nanos(*v as u64)),
_ => FindDurationItem::NotADuration,
},
FindItem::NotFound => FindDurationItem::NotFound,
}
}
pub fn to_duration_microseconds(&self) -> FindDurationItem {
match self {
FindItem::Found(ref v) => match v {
ItemKind::UInt(v) => FindDurationItem::Duration(Duration::from_micros(*v)),
ItemKind::Int(v) => FindDurationItem::Duration(Duration::from_micros(*v as u64)),
_ => FindDurationItem::NotADuration,
},
FindItem::NotFound => FindDurationItem::NotFound,
}
}
pub fn to_duration_milliseconds(&self) -> FindDurationItem {
match self {
FindItem::Found(ref v) => match v {
ItemKind::UInt(v) => FindDurationItem::Duration(Duration::from_millis(*v)),
ItemKind::Int(v) => FindDurationItem::Duration(Duration::from_millis(*v as u64)),
_ => FindDurationItem::NotADuration,
},
FindItem::NotFound => FindDurationItem::NotFound,
}
}
pub fn to_duration_seconds(&self) -> FindDurationItem {
match self {
FindItem::Found(ref v) => match v {
ItemKind::UInt(v) => FindDurationItem::Duration(Duration::from_secs(*v)),
ItemKind::Int(v) => FindDurationItem::Duration(Duration::from_secs(*v as u64)),
_ => FindDurationItem::NotADuration,
},
FindItem::NotFound => FindDurationItem::NotFound,
}
}
}
impl<'a> fmt::Display for FindItem<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::FindItem::*;
match self {
Found(ref v) => write!(f, "{}", v),
NotFound => write!(f, "<item not found>"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FindDurationItem {
NotFound,
NotADuration,
Duration(Duration),
}
impl FindDurationItem {
pub fn opt(self) -> Option<Duration> {
match self {
FindDurationItem::Duration(d) => Some(d),
_ => None,
}
}
}
impl fmt::Display for FindDurationItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::FindDurationItem::*;
match self {
Duration(d) => write!(f, "{:?}", d),
NotFound => write!(f, "<item not found>"),
NotADuration => write!(f, "<item not a duration>"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ItemKind {
Text(String),
Boolean(bool),
Float(f64),
UInt(u64),
Int(i64),
Snapshot(Snapshot),
}
impl ItemKind {
fn to_json_value(&self, config: &JsonConfig) -> JsonValue {
match *self {
ItemKind::Text(ref v) => v.clone().into(),
ItemKind::Boolean(v) => {
if config.make_booleans_ints {
if v {
1.into()
} else {
0.into()
}
} else {
v.into()
}
}
ItemKind::Float(v) => v.into(),
ItemKind::UInt(v) => v.into(),
ItemKind::Int(v) => v.into(),
ItemKind::Snapshot(ref snapshot) => snapshot.to_json_value(config),
}
}
}
impl fmt::Display for ItemKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::ItemKind::*;
match self {
Text(ref v) => write!(f, "{}", v),
Boolean(v) => write!(f, "{}", v),
Float(v) => write!(f, "{}", v),
UInt(v) => write!(f, "{}", v),
Int(v) => write!(f, "{}", v),
Snapshot(ref snapshot) => write!(f, "Snapshot({} items)", snapshot.items.len()),
}
}
}
impl From<ObservedValue> for Option<ItemKind> {
fn from(ov: ObservedValue) -> Self {
match ov {
ObservedValue::SignedInteger(v) => Some(v.into()),
ObservedValue::UnsignedInteger(v) => Some(v.into()),
ObservedValue::Float(v) => Some(v.into()),
ObservedValue::Bool(v) => Some(v.into()),
ObservedValue::Duration(_, _) => None,
ObservedValue::ChangedBy(_) => None,
}
}
}
pub struct JsonConfig {
pub make_booleans_ints: bool,
pub pretty: Option<u16>,
}
impl Default for JsonConfig {
fn default() -> JsonConfig {
JsonConfig {
make_booleans_ints: false,
pretty: None,
}
}
}
impl From<u64> for ItemKind {
fn from(what: u64) -> ItemKind {
ItemKind::UInt(what)
}
}
impl From<u32> for ItemKind {
fn from(what: u32) -> ItemKind {
ItemKind::UInt(what as u64)
}
}
impl From<u16> for ItemKind {
fn from(what: u16) -> ItemKind {
ItemKind::UInt(what as u64)
}
}
impl From<u8> for ItemKind {
fn from(what: u8) -> ItemKind {
ItemKind::UInt(what as u64)
}
}
impl From<usize> for ItemKind {
fn from(what: usize) -> ItemKind {
ItemKind::UInt(what as u64)
}
}
impl From<i64> for ItemKind {
fn from(what: i64) -> ItemKind {
ItemKind::Int(what)
}
}
impl From<i32> for ItemKind {
fn from(what: i32) -> ItemKind {
ItemKind::Int(what as i64)
}
}
impl From<i16> for ItemKind {
fn from(what: i16) -> ItemKind {
ItemKind::Int(what as i64)
}
}
impl From<i8> for ItemKind {
fn from(what: i8) -> ItemKind {
ItemKind::Int(what as i64)
}
}
impl From<isize> for ItemKind {
fn from(what: isize) -> ItemKind {
ItemKind::Int(what as i64)
}
}
impl From<String> for ItemKind {
fn from(what: String) -> ItemKind {
ItemKind::Text(what)
}
}
impl<'a> From<&'a str> for ItemKind {
fn from(what: &'a str) -> ItemKind {
ItemKind::Text(what.into())
}
}
impl From<f64> for ItemKind {
fn from(what: f64) -> ItemKind {
ItemKind::Float(what)
}
}
impl From<f32> for ItemKind {
fn from(what: f32) -> ItemKind {
ItemKind::Float(what as f64)
}
}
impl From<bool> for ItemKind {
fn from(what: bool) -> ItemKind {
ItemKind::Boolean(what)
}
}
impl From<Snapshot> for ItemKind {
fn from(what: Snapshot) -> ItemKind {
ItemKind::Snapshot(what)
}
}