1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#![deny(missing_docs)]
#![deny(warnings)]
#![deny(unsafe_code)]
//! Core types for influxive crates. The main point of this crate is to expose
//! the [MetricWriter] trait to be used by downstream influxive crates.
//!
//! ## Example [Metric] type creation:
//!
//! ```
//! let _metric = influxive_core::Metric::new(std::time::SystemTime::now(), "my.name")
//!     .with_field("field.bool", true)
//!     .with_field("field.float", 3.14)
//!     .with_field("field.signed", -42)
//!     .with_field("field.unsigned", 42)
//!     .with_field("field.string", "string.value")
//!     .with_tag("tag.bool", true)
//!     .with_tag("tag.float", 3.14)
//!     .with_tag("tag.signed", -42)
//!     .with_tag("tag.unsigned", 42)
//!     .with_tag("tag.string", "string.value");
//! ```

use std::borrow::Cow;
use std::sync::Arc;

/// Standin until std::io::Error::other is stablized.
pub fn err_other<E>(error: E) -> std::io::Error
where
    E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
    std::io::Error::new(std::io::ErrorKind::Other, error.into())
}

/// String type handling various string types usable by InfluxDB.
#[derive(Debug, Clone)]
pub enum StringType {
    /// String value.
    String(Cow<'static, str>),

    /// String value.
    ArcString(Arc<str>),
}

impl StringType {
    /// Get an owned string out of this StringType.
    pub fn into_string(self) -> String {
        match self {
            StringType::String(s) => s.into_owned(),
            StringType::ArcString(s) => s.to_string(),
        }
    }
}

macro_rules! stringtype_from_impl {
    ($($f:ty, $i:ident, $b:block,)*) => {$(
        impl From<$f> for StringType {
            fn from($i: $f) -> Self $b
        }
    )*};
}

stringtype_from_impl! {
    String, f, { StringType::String(Cow::Owned(f)) },
    &'static String, f, { StringType::String(Cow::Borrowed(f.as_str())) },
    &'static str, f, { StringType::String(Cow::Borrowed(f)) },
    Cow<'static, str>, f, { StringType::String(f) },
    Arc<str>, f, { StringType::ArcString(f) },
}

/// Field-type enum for sending data to InfluxDB.
#[derive(Debug, Clone)]
pub enum DataType {
    /// Bool value.
    Bool(bool),

    /// Float value.
    F64(f64),

    /// Signed integer value.
    I64(i64),

    /// Unsigned integer value.
    U64(u64),

    /// String value.
    String(StringType),
}

macro_rules! datatype_from_impl {
    ($($f:ty, $i:ident, $b:block,)*) => {$(
        impl From<$f> for DataType {
            fn from($i: $f) -> Self $b
        }
    )*};
}

datatype_from_impl! {
    bool, f, { DataType::Bool(f) },
    f64, f, { DataType::F64(f) },
    f32, f, { DataType::F64(f as f64) },
    i8, f, { DataType::I64(f as i64) },
    i16, f, { DataType::I64(f as i64) },
    i32, f, { DataType::I64(f as i64) },
    i64, f, { DataType::I64(f as i64) },
    u8, f, { DataType::U64(f as u64) },
    u16, f, { DataType::U64(f as u64) },
    u32, f, { DataType::U64(f as u64) },
    u64, f, { DataType::U64(f as u64) },
    String, f, { DataType::String(f.into()) },
    &'static String, f, { DataType::String(f.into()) },
    &'static str, f, { DataType::String(f.into()) },
    Cow<'static, str>, f, { DataType::String(f.into()) },
    Arc<str>, f, { DataType::String(f.into()) },
}

/// A metric to record in the influxdb instance.
#[derive(Debug)]
pub struct Metric {
    /// The timestamp for this metric report.
    pub timestamp: std::time::SystemTime,

    /// The name of this metric report.
    pub name: StringType,

    /// The fields associated with this metric report.
    pub fields: Vec<(StringType, DataType)>,

    /// The tags associated with this metric report.
    pub tags: Vec<(StringType, DataType)>,
}

impl Metric {
    /// Construct a new metric report to be sent to InfluxDB.
    pub fn new<N: Into<StringType>>(
        timestamp: std::time::SystemTime,
        name: N,
    ) -> Metric {
        Self {
            timestamp,
            name: name.into(),
            fields: Vec::new(),
            tags: Vec::new(),
        }
    }

    /// Add a field to this metric report.
    pub fn with_field<N, V>(mut self, name: N, value: V) -> Self
    where
        N: Into<StringType>,
        V: Into<DataType>,
    {
        self.fields.push((name.into(), value.into()));
        self
    }

    /// Add a tag to this metric report.
    pub fn with_tag<N, V>(mut self, name: N, value: V) -> Self
    where
        N: Into<StringType>,
        V: Into<DataType>,
    {
        self.tags.push((name.into(), value.into()));
        self
    }
}

/// Indicates a type that is capable of writing metrics to an InfluxDB instance.
pub trait MetricWriter: 'static + Send + Sync {
    /// Write a metric to an InfluxDB instance. Note, this should return
    /// immediately, perhaps by adding the metric to a memory buffer for
    /// a different process/task/thread to actually write the metric as
    /// determined by the concrete implementation.
    fn write_metric(&self, metric: Metric);
}