Documentation
//use eva_common::prelude::*;
use log::error;
use serde::{Deserialize, Deserializer, Serializer};
use std::str::FromStr;
use std::sync::atomic;
use std::sync::Arc;
use std::time::Duration;
use std::time::Instant;

pub fn get_eva_dir() -> String {
    std::env::var("EVA_DIR").unwrap_or_else(|_| "/opt/eva4".to_owned())
}

pub fn de_float_as_duration<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
    D: Deserializer<'de>,
{
    Ok(Duration::from_secs_f64(f64::deserialize(deserializer)?))
}

#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
pub fn de_float_as_duration_us<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
    D: Deserializer<'de>,
{
    Ok(Duration::from_nanos(
        (f64::deserialize(deserializer)? * 1000.0) as u64,
    ))
}

pub fn arc_atomic_true() -> Arc<atomic::AtomicBool> {
    Arc::new(atomic::AtomicBool::new(true))
}

pub fn serialize_uptime<S>(value: &Instant, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    serializer.serialize_f64(value.elapsed().as_secs_f64())
}

pub fn serialize_time_now<S>(_value: &(), serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    serializer.serialize_f64(crate::time::Time::now().into())
}

#[derive(Debug)]
pub enum SocketPath {
    Tcp(String),
    Udp(String),
    Unix(String),
}

impl FromStr for SocketPath {
    type Err = crate::Error;

    /// # Panics
    ///
    /// Will panic on internal errors
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(if s.starts_with("tcp://") {
            SocketPath::Tcp(s.strip_prefix("tcp://").unwrap().to_owned())
        } else if s.starts_with("udp://") {
            SocketPath::Udp(s.strip_prefix("udp://").unwrap().to_owned())
        } else {
            SocketPath::Unix(s.to_owned())
        })
    }
}

/// # Panics
///
/// Will panic of neither path nor default specified
pub fn format_path(base: &str, path: Option<&str>, default: Option<&str>) -> String {
    if let Some(p) = path {
        if p.starts_with('/') {
            p.to_owned()
        } else {
            format!("{}/{}", base, p)
        }
    } else if let Some(d) = default {
        format!("{}/{}", base, d)
    } else {
        panic!("unable to format, neither path nor default specified");
    }
}

pub trait ErrLogger {
    fn log_err(self) -> Self;
}

impl<R, E> ErrLogger for Result<R, E>
where
    E: std::fmt::Display,
{
    #[inline]
    fn log_err(self) -> Self {
        if let Err(ref e) = self {
            error!("{}", e);
        }
        self
    }
}