tometrics 0.1.0

Simple metrics abstraction
Documentation
pub mod influxdb;

use influxdb::InfluxDBClient;
use std::collections::HashMap;

#[derive(Debug)]
pub enum ClientTypes {
    InfluxDB,
}

// TODO should we export this?
macro_rules! hashmap {
    ($( $key: expr => $val: expr ),*) => {{
         let mut map = ::std::collections::HashMap::new();
         $( map.insert($key, $val); )*
         map
    }}
}

//
// A wrapper for easily pushing metrics to a backend.
//
// Note: current implementation is not mean for high frequency pushing (no buffering is used)
// @param bucket string The bucket/table/database we are pushing data into.
// @param static_metric_name string If we only are pushing one type of value, we can optionally just set the name once
// @param static_metric_metadata map If there are tags that do not change for all the data we are pushing, we can set those once
//
pub struct ToMetrics {
    backend_url: String,
    client_type: ClientTypes,

    bucket: String,
    static_metric_name: String,
    static_metric_metadata: HashMap<String, serde_json::Value>,

    username: Option<String>,
    password: Option<String>,
    verbose: Option<bool>,

    client: InfluxDBClient, // TODO we need to support multiple types,
}

impl ToMetrics {
    pub async fn new(
        backend_url: String,
        client_type: ClientTypes,
        bucket: String,
        static_metric_name: String, // Support the others with a builder for options.
    ) -> Self {
        // TODO put a lot of this into an init function?? meh

        if true {
            // Support configurability of this verbosity
            println!("[ToMetrics] Initializing \"{bucket}|{static_metric_name}\" streaming to {:?} via \"{backend_url}\"", client_type);
        }

        // TODO support stripping the trailing / if it was provided
        let formatted_backend_url = backend_url;

        // Setup the client
        let client = match client_type {
            ClientTypes::InfluxDB => {
                InfluxDBClient::new(formatted_backend_url.clone(), bucket.clone())
            }
        };

        client.init().await;

        ToMetrics {
            backend_url: formatted_backend_url,
            client_type: client_type,
            bucket: bucket,
            static_metric_name: static_metric_name,
            static_metric_metadata: HashMap::<String, serde_json::Value>::new(),
            username: None,
            password: None,
            verbose: None,
            client: client,
        }
    }

    //
    // Sends a metric: Simply delegate to the client to send a datum (or multiple).
    //
    // @param data Dict of key value pairs that holds the data/fields for our sample
    // @param metadata Dict of key value pairs that holds the metadata describing our sample
    // @param timestamp_override_ns int If a different timestamp is desired. Nanoseconds since epoch.
    pub async fn send(
        &self,
        data: HashMap<String, serde_json::Value>,
        metadata: HashMap<String, serde_json::Value>,
    ) -> bool {
        // TODO Support builder here. // TODO support timestamp_override_ns as an option too.
        let timestamp_override_ns = chrono::offset::Utc::now().timestamp_nanos() as usize;
        // const all_metadata = { ...this._static_metric_metadata, ...metadata }
        let mut all_metadata = self.static_metric_metadata.clone();

        all_metadata.extend(metadata.into_iter());

                // Delegate to client
                // TODO support nice return codes
        self.client.send(self.static_metric_name.clone(), timestamp_override_ns, data, all_metadata).await
    }
}

    //
    // TESTS
    //
    #[tokio::test]
    async fn push_metric_test() {
        assert_eq!(2 + 2, 4);
        //
        // Create the client
        //
        let tom = ToMetrics::new(
            "http://influx_hostname:8086".to_string(),
            ClientTypes::InfluxDB,
            "testdb".to_string(),
            "test_metric".to_string()).await;
            // assert!(tom);
        // assert!(tom.init().await);

        //
        // Push a metric
        //
        assert!(tom.send(
            // "test1".to_string(),
            // chrono::offset::Utc::now().timestamp_nanos() as usize, // Unique so it doesnt cache it and pushes each time. 
            hashmap!["vala".to_string()=>serde_json::json!(1)],
            hashmap!["tag1".to_string()=>serde_json::json!("asdf")],
        ).await);
    }