Skip to main content

influxlp_tools/
element.rs

1//! Elements are whats makes up the individual parts of a line protocol string
2//!
3//! # Line Protocol
4//!
5//! ```text
6//! measurement         tag set             field set              timestamp
7//! ----------- ------------------- ------------------------- -------------------
8//! measurement,tag1=val1,tag2=val2 field1="val1",field2=true 1729270461612452700
9//! ```
10//!
11//! ## Explanation
12//! - measurement: The measurement name
13//! - tag set: Optional key value pairs used to filter data points
14//! - field set: Required key value pairs containing the data point data
15//! - timestamp: Optional unix timestamp
16
17use std::{fmt::Display, str::FromStr};
18
19use anyhow::Context;
20use regex::Regex;
21
22use crate::traits::{Convert, Format};
23
24#[derive(Debug, Clone, Hash, PartialEq, Eq)]
25pub struct Measurement(pub String);
26
27impl From<&str> for Measurement {
28    fn from(value: &str) -> Self {
29        Measurement(value.to_string())
30    }
31}
32
33impl From<&String> for Measurement {
34    fn from(value: &String) -> Self {
35        Measurement(value.to_owned())
36    }
37}
38
39impl From<String> for Measurement {
40    fn from(value: String) -> Self {
41        Measurement(value)
42    }
43}
44
45impl Display for Measurement {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        write!(f, "{}", self.0)
48    }
49}
50
51impl Convert for Measurement {
52    /// Attempt to parse a generic type into [Measurement]
53    ///
54    /// This will always work for any generic type that implements the
55    /// `ToString` trait so it can be safely unwrapped
56    ///
57    /// # Example
58    /// ```rust
59    /// let uuid = Uuid::new_v4();
60    /// let measurement = Measurement::parse_from(uuid).unwrap();
61    /// ```
62    fn parse_from<T>(from: T) -> anyhow::Result<Self>
63    where
64        Self: Sized,
65        T: ToString,
66    {
67        Ok(Measurement(from.to_string()))
68    }
69
70    /// Attempt to parse [Measurement] into generic type T
71    ///
72    /// # Example
73    /// ```rust
74    /// let measurement = Measurement::String("d5a47b74-bff6-4dc5-9c7c-2558bd98a70b");
75    /// let uuid = key.parse_into<Uuid>().unwrap();
76    /// ```
77    fn parse_into<T>(&self) -> anyhow::Result<T>
78    where
79        T: FromStr,
80        <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
81    {
82        let t = self.0.parse::<T>()?;
83        Ok(t)
84    }
85}
86
87impl Format for Measurement {
88    fn escape(&self) -> Self {
89        Measurement(self.0.replace(" ", r"\ ").replace(",", r"\,"))
90    }
91
92    fn unescape(&self) -> Self {
93        Measurement(self.0.replace(r"\,", ",").replace(r"\ ", " "))
94    }
95}
96
97#[derive(Debug, Clone, Hash, PartialEq, Eq)]
98pub struct TagKey(pub String);
99
100impl From<&str> for TagKey {
101    fn from(value: &str) -> Self {
102        TagKey(value.to_string())
103    }
104}
105
106impl From<&String> for TagKey {
107    fn from(value: &String) -> Self {
108        TagKey(value.to_owned())
109    }
110}
111
112impl From<String> for TagKey {
113    fn from(value: String) -> Self {
114        TagKey(value)
115    }
116}
117
118impl Display for TagKey {
119    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120        write!(f, "{}", self.0)
121    }
122}
123
124impl Convert for TagKey {
125    /// Attempt to parse a generic type into [TagKey]
126    ///
127    /// This will always work for any generic type that implements the
128    /// `ToString` trait so it can be safely unwrapped
129    ///
130    /// # Example
131    /// ```rust
132    /// let uuid = Uuid::new_v4();
133    /// let key = TagKey::parse_from(uuid).unwrap();
134    /// ```
135    fn parse_from<T>(from: T) -> anyhow::Result<Self>
136    where
137        Self: Sized,
138        T: ToString,
139    {
140        Ok(TagKey(from.to_string()))
141    }
142
143    /// Attempt to parse [TagKey] into generic type T
144    ///
145    /// # Example
146    /// ```rust
147    /// let key = TagKey::String("d5a47b74-bff6-4dc5-9c7c-2558bd98a70b");
148    /// let uuid = key.parse_into<Uuid>().unwrap();
149    /// ```
150    fn parse_into<T>(&self) -> anyhow::Result<T>
151    where
152        T: FromStr,
153        <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
154    {
155        let t = self.0.parse::<T>()?;
156        Ok(t)
157    }
158}
159
160impl Format for TagKey {
161    fn escape(&self) -> Self {
162        TagKey(
163            self.0
164                .replace(" ", r"\ ")
165                .replace(",", r"\,")
166                .replace("=", r"\="),
167        )
168    }
169
170    fn unescape(&self) -> Self {
171        TagKey(
172            self.0
173                .replace(r"\=", "=")
174                .replace(r"\,", ",")
175                .replace(r"\ ", " "),
176        )
177    }
178}
179
180#[derive(Debug, Clone, PartialEq, Eq)]
181pub struct TagValue(pub String);
182
183impl From<&str> for TagValue {
184    fn from(value: &str) -> Self {
185        TagValue(value.to_string())
186    }
187}
188
189impl From<&String> for TagValue {
190    fn from(value: &String) -> Self {
191        TagValue(value.to_owned())
192    }
193}
194
195impl From<String> for TagValue {
196    fn from(value: String) -> Self {
197        TagValue(value)
198    }
199}
200
201impl Display for TagValue {
202    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203        write!(f, "{}", self.0)
204    }
205}
206
207impl Convert for TagValue {
208    /// Attempt to parse a generic type into [TagValue]
209    ///
210    /// This will always work for any generic type that implements the
211    /// `ToString` trait so it can be safely unwrapped
212    ///
213    /// # Example
214    /// ```rust
215    /// let uuid = Uuid::new_v4();
216    /// let value = TagValue::parse_from(uuid).unwrap();
217    /// ```
218    fn parse_from<T>(from: T) -> anyhow::Result<Self>
219    where
220        Self: Sized,
221        T: ToString,
222    {
223        Ok(TagValue(from.to_string()))
224    }
225
226    /// Attempt to parse [TagValue] into generic type T
227    ///
228    /// # Example
229    /// ```rust
230    /// let value = TagValue::String("d5a47b74-bff6-4dc5-9c7c-2558bd98a70b");
231    /// let uuid = value.parse_into<Uuid>().unwrap();
232    /// ```
233    fn parse_into<T>(&self) -> anyhow::Result<T>
234    where
235        T: FromStr,
236        <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
237    {
238        let t = self.0.parse::<T>()?;
239        Ok(t)
240    }
241}
242
243impl Format for TagValue {
244    fn escape(&self) -> Self {
245        TagValue(
246            self.0
247                .replace(" ", r"\ ")
248                .replace(",", r"\,")
249                .replace("=", r"\="),
250        )
251    }
252
253    fn unescape(&self) -> Self {
254        TagValue(
255            self.0
256                .replace(r"\=", "=")
257                .replace(r"\,", ",")
258                .replace(r"\ ", " "),
259        )
260    }
261}
262
263#[derive(Debug, Clone, Hash, PartialEq, Eq)]
264pub struct FieldKey(pub String);
265
266impl From<&str> for FieldKey {
267    fn from(value: &str) -> Self {
268        FieldKey(value.to_string())
269    }
270}
271
272impl From<&String> for FieldKey {
273    fn from(value: &String) -> Self {
274        FieldKey(value.to_owned())
275    }
276}
277
278impl From<String> for FieldKey {
279    fn from(value: String) -> Self {
280        FieldKey(value)
281    }
282}
283
284impl Display for FieldKey {
285    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286        write!(f, "{}", self.0)
287    }
288}
289
290impl Convert for FieldKey {
291    /// Attempt to parse a generic type into [FieldKey]
292    ///
293    /// This will always work for any generic type that implements the
294    /// `ToString` trait so it can be safely unwrapped
295    ///
296    /// # Example
297    /// ```rust
298    /// let uuid = Uuid::new_v4();
299    /// let key = FieldKey::parse_from(uuid).unwrap();
300    /// ```
301    fn parse_from<T>(from: T) -> anyhow::Result<Self>
302    where
303        Self: Sized,
304        T: ToString,
305    {
306        Ok(FieldKey(from.to_string()))
307    }
308
309    /// Attempt to parse [FieldKey] into generic type T
310    ///
311    /// # Example
312    /// ```rust
313    /// let key = FieldKey::String("d5a47b74-bff6-4dc5-9c7c-2558bd98a70b");
314    /// let uuid = key.parse_into<Uuid>().unwrap();
315    /// ```
316    fn parse_into<T>(&self) -> anyhow::Result<T>
317    where
318        T: FromStr,
319        <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
320    {
321        let t = self.0.parse::<T>()?;
322        Ok(t)
323    }
324}
325
326impl Format for FieldKey {
327    fn escape(&self) -> Self {
328        FieldKey(
329            self.0
330                .replace(" ", r"\ ")
331                .replace(",", r"\,")
332                .replace("=", r"\="),
333        )
334    }
335
336    fn unescape(&self) -> Self {
337        FieldKey(
338            self.0
339                .replace(r"\=", "=")
340                .replace(r"\,", ",")
341                .replace(r"\ ", " "),
342        )
343    }
344}
345
346#[derive(Debug, Clone)]
347pub enum FieldValue {
348    /// Represent a floating point number field value
349    Float(f64),
350
351    /// Represent a signed integer number field value
352    Integer(i64),
353
354    /// Represent an unsigned integer number field value
355    UInteger(u64),
356
357    /// Represent a string field value
358    String(String),
359
360    /// Represent a boolean field value
361    Boolean(bool),
362}
363
364impl From<&str> for FieldValue {
365    fn from(value: &str) -> Self {
366        FieldValue::String(value.to_owned())
367    }
368}
369
370impl From<&String> for FieldValue {
371    fn from(value: &String) -> Self {
372        FieldValue::String(value.to_owned())
373    }
374}
375
376impl From<String> for FieldValue {
377    fn from(value: String) -> Self {
378        FieldValue::String(value)
379    }
380}
381
382impl From<f32> for FieldValue {
383    fn from(value: f32) -> Self {
384        FieldValue::Float(value.into())
385    }
386}
387
388impl From<f64> for FieldValue {
389    fn from(value: f64) -> Self {
390        FieldValue::Float(value)
391    }
392}
393
394impl From<i8> for FieldValue {
395    fn from(value: i8) -> Self {
396        FieldValue::Integer(value.into())
397    }
398}
399
400impl From<i16> for FieldValue {
401    fn from(value: i16) -> Self {
402        FieldValue::Integer(value.into())
403    }
404}
405
406impl From<i32> for FieldValue {
407    fn from(value: i32) -> Self {
408        FieldValue::Integer(value.into())
409    }
410}
411
412impl From<i64> for FieldValue {
413    fn from(value: i64) -> Self {
414        FieldValue::Integer(value)
415    }
416}
417
418impl From<u8> for FieldValue {
419    fn from(value: u8) -> Self {
420        FieldValue::UInteger(value.into())
421    }
422}
423
424impl From<u16> for FieldValue {
425    fn from(value: u16) -> Self {
426        FieldValue::UInteger(value.into())
427    }
428}
429
430impl From<u32> for FieldValue {
431    fn from(value: u32) -> Self {
432        FieldValue::UInteger(value.into())
433    }
434}
435
436impl From<u64> for FieldValue {
437    fn from(value: u64) -> Self {
438        FieldValue::UInteger(value)
439    }
440}
441
442impl From<bool> for FieldValue {
443    fn from(value: bool) -> Self {
444        FieldValue::Boolean(value)
445    }
446}
447
448impl Display for FieldValue {
449    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
450        let value = match self {
451            FieldValue::Float(number) => format!("{number}"),
452            FieldValue::Integer(number) => format!("{number}i"),
453            FieldValue::UInteger(number) => format!("{number}i"),
454            FieldValue::String(string) => format!("{string}"),
455            FieldValue::Boolean(boolean) => format!("{boolean}"),
456        };
457
458        write!(f, "{}", value)
459    }
460}
461
462impl PartialEq for FieldValue {
463    fn eq(&self, other: &Self) -> bool {
464        self.to_string() == other.to_string()
465    }
466}
467
468impl Convert for FieldValue {
469    /// Attempt to parse a generic type into [FieldValue]
470    ///
471    /// # Example
472    /// ```rust
473    /// let uuid = Uuid::new_v4();
474    /// let value = FieldValue::parse_from(uuid).unwrap();
475    /// ```
476    fn parse_from<T>(from: T) -> anyhow::Result<Self>
477    where
478        Self: Sized,
479        T: ToString,
480    {
481        let s = from.to_string();
482
483        // Check if string is a number that ends with an i
484        let re = Regex::new(r"^-?\d+i$").unwrap();
485        if re.is_match(&s) {
486            // Remove the `i`
487            let mut number = s.to_string();
488            number.pop();
489
490            let value = match number.starts_with("-") {
491                true => {
492                    let int = number
493                        .parse::<i64>()
494                        .with_context(|| format!("number {s} is not a valid integer"))?;
495
496                    FieldValue::Integer(int)
497                }
498                false => {
499                    let uint = number
500                        .parse::<u64>()
501                        .with_context(|| format!("number {s} is not a valid unsigned integer"))?;
502
503                    FieldValue::UInteger(uint)
504                }
505            };
506
507            return Ok(value);
508        };
509
510        // Check if string is a float or just a regular number without and `i`
511        if let Ok(number) = s.parse::<f64>() {
512            return Ok(FieldValue::Float(number));
513        }
514
515        // Check if its a boolean, else treat as a string
516        let value = match s.as_ref() {
517            "t" | "T" | "true" | "True" | "TRUE" => FieldValue::Boolean(true),
518            "f" | "F" | "false" | "False" | "FALSE" => FieldValue::Boolean(false),
519            _ => FieldValue::String(s.to_string()),
520        };
521
522        Ok(value)
523    }
524
525    /// Attempt to parse [FieldValue] into generic type T
526    ///
527    /// Note: This only makes sense to do if type is [FieldValue::String]
528    ///
529    /// # Example
530    /// ```rust
531    /// let value = FieldValue::String("d5a47b74-bff6-4dc5-9c7c-2558bd98a70b");
532    /// let uuid = value.parse_into<Uuid>().unwrap();
533    /// ```
534    fn parse_into<T>(&self) -> anyhow::Result<T>
535    where
536        T: FromStr,
537        <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
538    {
539        let r = match self {
540            FieldValue::Float(number) => number.to_string(),
541            FieldValue::Integer(number) => number.to_string(),
542            FieldValue::UInteger(number) => number.to_string(),
543            FieldValue::String(string) => string.to_string(),
544            FieldValue::Boolean(bool) => bool.to_string(),
545        }
546        .parse::<T>()?;
547
548        Ok(r)
549    }
550}
551
552impl Format for FieldValue {
553    fn escape(&self) -> Self {
554        match self {
555            FieldValue::String(string) => {
556                let escaped = string.replace("\\", "\\\\").replace("\"", "\\\"");
557                FieldValue::String(format!("\"{escaped}\""))
558            }
559            other => other.clone(),
560        }
561    }
562
563    fn unescape(&self) -> Self {
564        match self {
565            FieldValue::String(string) => {
566                let unescaped = match string.starts_with("\"") && string.ends_with("\"") {
567                    true => &string[1..string.len() - 1],
568                    false => string.as_str(),
569                };
570                FieldValue::String(unescaped.replace("\\\"", "\"").replace("\\\\", "\\"))
571            }
572            other => other.clone(),
573        }
574    }
575}
576
577#[cfg(test)]
578mod test {
579    use super::*;
580
581    #[test]
582    fn test_tag_key_escape_unescape() {
583        let key = TagKey::from("some, value=");
584        let escaped_key = key.escape();
585
586        assert_eq!(escaped_key.to_string(), "some\\,\\ value\\=");
587
588        let unescaped_key = escaped_key.unescape();
589        assert_eq!(unescaped_key.to_string(), "some, value=");
590    }
591
592    #[test]
593    fn test_tag_value_escape_unescape() {
594        let value = TagValue::from("some, value=");
595        let escaped_value = value.escape();
596
597        assert_eq!(escaped_value.to_string(), "some\\,\\ value\\=");
598
599        let unescaped_value = escaped_value.unescape();
600        assert_eq!(unescaped_value.to_string(), "some, value=");
601    }
602
603    #[test]
604    fn test_field_key_escape_unescape() {
605        let key = FieldKey::from("some, value=");
606        let escaped_key = key.escape();
607
608        assert_eq!(escaped_key.to_string(), "some\\,\\ value\\=");
609
610        let unescaped_key = escaped_key.unescape();
611        assert_eq!(unescaped_key.to_string(), "some, value=");
612    }
613
614    #[test]
615    fn test_field_value_escape_unescape() {
616        // Only strings are escaped, every other value is as is
617        let value = FieldValue::from("{\"foo\": [\"bar=\\baz\"]}");
618        let escaped_value = value.escape();
619
620        assert_eq!(
621            escaped_value.to_string(),
622            "\"{\\\"foo\\\": [\\\"bar=\\\\baz\\\"]}\""
623        );
624
625        let unescaped_value = escaped_value.unescape();
626        assert_eq!(unescaped_value.to_string(), "{\"foo\": [\"bar=\\baz\"]}");
627    }
628
629    #[test]
630    fn test_field_value_parse_float() {
631        let parsed = FieldValue::parse_from("10.0").unwrap();
632        let expected = FieldValue::Float(10.);
633        assert_eq!(parsed, expected)
634    }
635
636    #[test]
637    fn test_field_value_parse_signed_integer() {
638        let parsed = FieldValue::parse_from("-10i").unwrap();
639        let expected = FieldValue::Integer(-10);
640        assert_eq!(parsed, expected);
641
642        let parsed = FieldValue::parse_from("10i").unwrap();
643        let expected = FieldValue::Integer(10);
644        assert_eq!(parsed, expected)
645    }
646
647    #[test]
648    fn test_field_value_parse_unsigned_integer() {
649        // Only if a number cannot fit in an i64 it will parsed into a u64
650        let parsed = FieldValue::parse_from("9223372036854775808i").unwrap();
651        let expected = FieldValue::UInteger(9223372036854775808);
652        assert_eq!(parsed, expected);
653    }
654
655    #[test]
656    fn test_field_value_parse_boolean() {
657        let true_variants = vec!["t", "T", "true", "True", "TRUE"];
658        for variant in true_variants {
659            let parsed = FieldValue::parse_from(variant).unwrap();
660            let expected = FieldValue::Boolean(true);
661            assert_eq!(parsed, expected);
662        }
663
664        let false_variants = vec!["f", "F", "false", "False", "FALSE"];
665        for variant in false_variants {
666            let parsed = FieldValue::parse_from(variant).unwrap();
667            let expected = FieldValue::Boolean(false);
668            assert_eq!(parsed, expected);
669        }
670    }
671
672    #[test]
673    fn test_field_value_display() {
674        assert_eq!(FieldValue::Float(10.0).to_string(), "10");
675        assert_eq!(FieldValue::Float(10.5).to_string(), "10.5");
676        assert_eq!(FieldValue::Integer(10).to_string(), "10i");
677        assert_eq!(FieldValue::UInteger(10).to_string(), "10i");
678        assert_eq!(FieldValue::String("hello".to_string()).to_string(), "hello");
679        assert_eq!(FieldValue::Boolean(true).to_string(), "true");
680        assert_eq!(FieldValue::Boolean(false).to_string(), "false");
681    }
682}