glowmarkt 0.5.3

Access to the Glowmarkt API for smart meter data.
Documentation
use std::{collections::BTreeMap, fmt};

use glowmarkt::{Device, Resource};
use time::{OffsetDateTime, UtcOffset};

pub struct Measurement {
    pub id: String,
    pub timestamp: i128,
    pub tags: BTreeMap<String, String>,
    pub fields: BTreeMap<String, f64>,
}

impl Measurement {
    pub fn new(id: &str, timestamp: OffsetDateTime, tags: BTreeMap<String, String>) -> Self {
        Measurement {
            id: id.to_owned(),
            timestamp: timestamp.to_offset(UtcOffset::UTC).unix_timestamp_nanos(),
            tags,
            fields: BTreeMap::new(),
        }
    }

    pub fn add_field(&mut self, key: &str, value: f64) {
        assert!(value.is_finite());

        self.fields.insert(key.to_owned(), value);
    }
}

impl fmt::Display for Measurement {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        assert!(!self.fields.is_empty());

        let tags = self
            .tags
            .iter()
            .map(|(k, v)| format!("{}={}", escape(k), escape(v)))
            .collect::<Vec<String>>();

        let fields = self
            .fields
            .iter()
            .map(|(k, v)| format!("{}={}", escape(k), v))
            .collect::<Vec<String>>();

        if !tags.is_empty() {
            f.pad(&format!(
                "{},{} {} {}",
                self.id,
                tags.join(","),
                fields.join(","),
                self.timestamp
            ))
        } else {
            f.pad(&format!(
                "{} {} {}",
                self.id,
                fields.join(","),
                self.timestamp
            ))
        }
    }
}

pub fn add_tags_for_device(tags: &mut BTreeMap<String, String>, device: &Device) {
    tags.insert("device-id".to_string(), device.id.clone());
    if let Some(ref description) = device.description {
        tags.insert("device".to_string(), description.clone());
    }
    tags.insert("device-active".to_string(), device.active.to_string());
    tags.insert("hardware-id".to_string(), device.hardware_id.to_string());
    for (k, v) in device.hardware_ids.iter() {
        tags.insert(k.clone(), v.clone());
    }
}

pub fn add_tags_for_resource(tags: &mut BTreeMap<String, String>, resource: &Resource) {
    tags.insert("resource-id".to_string(), resource.id.clone());
    tags.insert("resource".to_string(), resource.name.clone());
    tags.insert("resource-active".to_string(), resource.active.to_string());

    if let Some(ref classifier) = resource.classifier {
        tags.insert("classifier".to_string(), classifier.clone());
    }

    if let Some(ref unit) = resource.base_unit {
        tags.insert("unit".to_string(), unit.clone());
    }

    if let Some(ref classifier) = resource.classifier {
        if let Some(class) = classifier.split('.').next() {
            tags.insert("class".to_string(), class.to_string());
        }
    }
}

pub fn field_for_classifier(classifier: &Option<String>) -> &str {
    if let Some(classifier) = classifier {
        classifier.split('.').last().unwrap()
    } else {
        "value"
    }
}

fn escape(tag: &str) -> String {
    tag.replace(' ', "\\ ").replace(',', "\\,")
}