influxlp_tools/lib.rs
1//! InfluxDB V2 Line Protocol Tools is a parsing and building library for
2//! InfluxDB v2's line protocol. It provides easy-to-use functionality with
3//! built-in validation, support for a builder pattern and dynamic population,
4//! and options for modifying existing line protocols
5//!
6//! # Example
7//! Below is an example of building a line protocol string and parsing one
8//!
9//! ## Building a line protocol string
10//!
11//! At minimum the measurement name and a field is required to build a valid
12//! line protocol string
13//!
14//! ```rust
15//! let line_protocol = LineProtocol::new("measurement")
16//! .add_field("field", "value")
17//! .build()
18//! .unwrap();
19//! ```
20//!
21//! You can overwrite the measurement name by calling the `measurement` method
22//!
23//! ```rust
24//! let mut line_protocol = LineProtocol::new("measurement")
25//! .add_field("field", "value")
26//! .build()
27//! .unwrap();
28//!
29//! line_protocol = line_protocol.measurement("new_measurement");
30//! ```
31//!
32//! Multiple fields can be add by calling the `add_field` method multiple times
33//!
34//! ```rust
35//! let line_protocol = LineProtocol::new("measurement")
36//! .add_field("field1", "value")
37//! .add_field("field2", "value")
38//! .build()
39//! .unwrap();
40//! ```
41//!
42//! Optionally tags can be added. More tags can be added as with fields
43//!
44//! ```rust
45//! let line_protocol = LineProtocol::new("measurement")
46//! .add_tag("tag1", "value")
47//! .add_tag("tag2", "value")
48//! .add_field("field", "value")
49//! .build()
50//! .unwrap();
51//! ```
52//!
53//! A timestamp can be added with the `with_timestamp` method. By default the
54//! timestamp is defined in nanosecond precision. If you are using any other
55//! precision, e.g., seconds, it needs be defined when querying influx
56//!
57//! ```rust
58//! let line_protocol = LineProtocol::new("measurement")
59//! .add_field("field", "value")
60//! .with_timestamp(1729270461612452700i64)
61//! .build()
62//! .unwrap();
63//! ```
64//!
65//! A field, tag, and timestamp can be deleted if needed. This is done by
66//! calling the respective `delete` function
67//!
68//! ```rust
69//! let mut line_protocol = LineProtocol::new("measurement")
70//! .add_tag("tag", "value")
71//! .add_field("field", "value");
72//!
73//! line_protocol.delete_tag("tag")
74//! ```
75//!
76//! **Note:** that deleting all fields will cause the building to fail as
77//! atleast **one** field is required
78//!
79//! ## Parsing a line protocol string
80//!
81//! To parse a line protocol string the `parse_line` method can be used
82//!
83//! ```rust
84//! let line =
85//! "measurement,tag2=value,tag=value field=\"hello\",field2=\"world\" 1729270461612452700";
86//! let line_protocol = LineProtocol::parse_line(line).unwrap();
87//! ```
88//!
89//! To parse multiple lines seperated by a newline the `parse_lines` method can
90//! be used instead
91//!
92//! ```rust
93//! let lines = vec![
94//! "measurement,tag=value field=\"value\"",
95//! "measurement field=\"{\\\"test\\\": \\\"hello\\\"}\"",
96//! "measurement,tag2=value,tag=value field=\"value\",field2=\"{\\\"test\\\": \
97//! \\\"hello\\\"}\" 1729270461612452700",
98//! ]
99//! .join("\n");
100//!
101//! let result = LineProtocol::parse_lines(&lines);
102//! ```
103//!
104//! **Note:** The parsed line can be modified and rebuilt if needed
105
106use std::{collections::HashMap, fmt::Display};
107
108use element::{FieldKey, FieldValue, Measurement, TagKey, TagValue};
109
110pub mod builder;
111pub mod element;
112pub mod error;
113pub mod parser;
114pub mod traits;
115
116#[derive(Debug, Clone)]
117pub struct LineProtocol {
118 /// The data point measurement name
119 pub measurement: Measurement,
120
121 /// The data point tag set
122 pub tags: Option<HashMap<TagKey, TagValue>>,
123
124 /// The data point field set
125 pub fields: HashMap<FieldKey, FieldValue>,
126
127 /// To ensure a data point includes the time a metric is observed (not
128 /// received by InfluxDB), include a timestamp if not defined
129 ///
130 /// By default the timestamp is defined in nanoseconds. If you are using any
131 /// other form of precision it needs to be defined when making the insert
132 /// request
133 // Unfortunately there is no way of knowing the timestamp precision from just the given number
134 // as the precision is defined when you query the database. But the min/max timestamp value is
135 // exactly a i64 https://docs.influxdata.com/influxdb/cloud/reference/syntax/line-protocol/#unix-timestamp
136 pub timestamp: Option<i64>,
137}
138
139impl PartialEq for LineProtocol {
140 fn eq(&self, other: &Self) -> bool {
141 if self.measurement != other.measurement {
142 println!("name not equal");
143 return false;
144 }
145
146 let tags_matches = match (&self.tags, &other.tags) {
147 (Some(tags1), Some(tags2)) => tags1 == tags2,
148 (None, None) => true,
149 _ => return false,
150 };
151
152 let timestamp_matches = match (self.timestamp, other.timestamp) {
153 (Some(ts1), Some(ts2)) => ts1 == ts2,
154 (None, None) => true,
155 _ => return false,
156 };
157
158 // At this point we know the measurement is equal. If the tags and timestamp are
159 // also equal its a duplicate line
160 tags_matches && timestamp_matches
161 }
162}
163
164impl Display for LineProtocol {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 let lp = match &self.build() {
167 Ok(lp) => lp.to_string(),
168 Err(e) => format!("invalid line protocol: {e}"),
169 };
170
171 write!(f, "{}", lp)
172 }
173}
174
175impl LineProtocol {
176 /// Get a cloned version of the measurement
177 pub fn get_measurement(&self) -> Measurement {
178 self.measurement.clone()
179 }
180
181 /// Get a reference of the measurement
182 pub fn get_measurement_ref(&self) -> &Measurement {
183 &self.measurement
184 }
185
186 /// Get a mutable reference of the measurement
187 pub fn get_measurement_mut(&mut self) -> &mut Measurement {
188 &mut self.measurement
189 }
190
191 /// Get the tag value associated with the provided tag key
192 ///
193 /// # Args
194 /// * `key` - A [valid](https://docs.influxdata.com/influxdb/cloud/reference/syntax/line-protocol/#special-characters)
195 /// tag key
196 pub fn get_tag<K>(&self, key: K) -> Option<TagValue>
197 where
198 K: Into<TagKey>,
199 {
200 match &self.tags {
201 Some(tags) => tags.get(&key.into()).cloned(),
202 None => None,
203 }
204 }
205
206 /// Get a reference to the tag value associated with the provided tag key
207 ///
208 /// # Args
209 /// * `key` - A [valid](https://docs.influxdata.com/influxdb/cloud/reference/syntax/line-protocol/#special-characters)
210 /// tag key
211 pub fn get_tag_ref<K>(&self, key: K) -> Option<&TagValue>
212 where
213 K: Into<TagKey>,
214 {
215 match &self.tags {
216 Some(tags) => tags.get(&key.into()),
217 None => None,
218 }
219 }
220
221 /// Get a mutable reference to the tag value associated with the provided
222 /// tag key
223 ///
224 /// # Args
225 /// * `key` - A [valid](https://docs.influxdata.com/influxdb/cloud/reference/syntax/line-protocol/#special-characters)
226 /// tag key
227 pub fn get_tag_mut<K>(&mut self, key: K) -> Option<&mut TagValue>
228 where
229 K: Into<TagKey>,
230 {
231 match &mut self.tags {
232 Some(tags) => tags.get_mut(&key.into()),
233 None => None,
234 }
235 }
236
237 /// Get the field value associated with the provided field key
238 ///
239 /// # Args
240 /// * `key` - A [valid](https://docs.influxdata.com/influxdb/cloud/reference/syntax/line-protocol/#special-characters)
241 /// field key
242 pub fn get_field<K>(&self, key: K) -> Option<FieldValue>
243 where
244 K: Into<FieldKey>,
245 {
246 self.fields.get(&key.into()).cloned()
247 }
248
249 /// Get a reference to the field value associated with the provided field
250 /// key
251 ///
252 /// # Args
253 /// * `key` - A [valid](https://docs.influxdata.com/influxdb/cloud/reference/syntax/line-protocol/#special-characters)
254 /// field key
255 pub fn get_field_ref<K>(&self, key: K) -> Option<&FieldValue>
256 where
257 K: Into<FieldKey>,
258 {
259 self.fields.get(&key.into())
260 }
261
262 /// Get a mutable reference to the field value associated with the provided
263 /// field key
264 ///
265 /// # Args
266 /// * `key` - A [valid](https://docs.influxdata.com/influxdb/cloud/reference/syntax/line-protocol/#special-characters)
267 /// field key
268 pub fn get_field_mut<K>(&mut self, key: K) -> Option<&mut FieldValue>
269 where
270 K: Into<FieldKey>,
271 {
272 self.fields.get_mut(&key.into())
273 }
274
275 /// Get a cloned version of the timestamp
276 pub fn get_timestamp(&self) -> Option<i64> {
277 self.timestamp
278 }
279
280 /// Get a reference of the timestamp
281 pub fn get_timestamp_ref(&self) -> Option<&i64> {
282 self.timestamp.as_ref()
283 }
284
285 /// Get a mutable reference of the timestamp
286 pub fn get_timestamp_mut(&mut self) -> Option<&mut i64> {
287 self.timestamp.as_mut()
288 }
289}