rsiot/components_config/influxdb3/
line_protocol_item.rs

1use std::collections::HashMap;
2
3use crate::message::*;
4
5use super::FieldValue;
6
7/// Строка записи в данных в базу через HTTP API
8#[derive(Debug)]
9pub struct LineProtocolItem {
10    /// Table
11    pub table: String,
12
13    /// Словать Тег = Значение тега
14    pub tags: HashMap<String, String>,
15
16    /// Словать Поле = Значение поля
17    pub fields: HashMap<String, FieldValue>,
18
19    /// Метка времени
20    pub ts: Option<Timestamp>,
21}
22
23impl LineProtocolItem {
24    /// Новая строка записи
25    pub fn new_simple(table: &str, value: impl Into<FieldValue>) -> Self {
26        Self {
27            table: table.into(),
28            tags: HashMap::new(),
29            fields: HashMap::from([("value".to_string(), value.into())]),
30            ts: Some(Timestamp::default()),
31        }
32    }
33
34    /// Преобразование в строку, для отправки в базу данных
35    pub fn to_string(&self) -> Result<String, super::Error> {
36        let mut line = String::from("");
37
38        let table = &self.table;
39        line.push_str(table);
40
41        let tags = self
42            .tags
43            .iter()
44            .map(|(k, v)| format!("{k}={v}"))
45            .collect::<Vec<String>>()
46            .join(",");
47        if !tags.is_empty() {
48            line.push(',');
49            line.push_str(&tags);
50        }
51
52        let fields = self
53            .fields
54            .iter()
55            .map(|(k, v)| format!("{k}={v}"))
56            .collect::<Vec<String>>()
57            .join(",");
58        line.push(' ');
59        line.push_str(&fields);
60
61        if let Some(ts) = &self.ts {
62            let ts = ts
63                .timestamp_nanos_opt()
64                .ok_or(super::Error::WrongTimestamp(ts.clone()))?;
65            line.push(' ');
66            line.push_str(&ts.to_string());
67        }
68
69        Ok(line)
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use itertools::Itertools;
76
77    use super::*;
78
79    /// cargo test --features cmp_influxdb -- components_config::influxdb3::line_protocol_item::tests::test1 --exact --show-output
80    #[test]
81    fn test1() {
82        let lpi = LineProtocolItem {
83            table: "cpu".to_string(),
84            tags: HashMap::from([
85                ("host".to_string(), "Alpha".to_string()),
86                ("region".to_string(), "us-west".to_string()),
87                ("application".to_string(), "webserver".to_string()),
88            ]),
89            fields: HashMap::from([
90                ("val".to_string(), FieldValue::from(1)),
91                ("usage_percent".to_string(), FieldValue::from(20.5)),
92                ("status".to_string(), FieldValue::from("OK".to_string())),
93            ]),
94            ts: None,
95        };
96
97        let lpi: String = lpi.to_string().unwrap();
98
99        println!("line protocol: {}", lpi);
100
101        let from_manual = r#"cpu,host=Alpha,region=us-west,application=webserver val=1i,usage_percent=20.5,status="OK""#;
102        println!("from manual:   {}", from_manual);
103
104        assert_eq!(
105            lpi.chars().sorted().rev().collect::<String>(),
106            from_manual.chars().sorted().rev().collect::<String>()
107        );
108    }
109}