influxdb_line_protocol/
lib.rs

1//! This crate contains pure Rust implementations of
2//!
3//! 1. A [parser](crate::parse_lines) for [InfluxDB Line Protocol] developed as part of the
4//! [InfluxDB IOx] project.  This implementation is intended to be
5//! compatible with the [Go implementation], however, this
6//! implementation uses a [nom] combinator-based parser rather than
7//! attempting to port the imperative Go logic so there are likely
8//! some small differences.
9//!
10//! 2. A [builder](crate::builder::LineProtocolBuilder) to construct valid [InfluxDB Line Protocol]
11//!
12//! # Example
13//!
14//! Here is an example of how to parse the following line
15//! protocol data into a `ParsedLine`:
16//!
17//! ```text
18//!cpu,host=A,region=west usage_system=64.2 1590488773254420000
19//!```
20//!
21//! ```
22//! use influxdb_line_protocol::{ParsedLine, FieldValue};
23//!
24//! let mut parsed_lines =
25//!     influxdb_line_protocol::parse_lines(
26//!         "cpu,host=A,region=west usage_system=64i 1590488773254420000"
27//!     );
28//! let parsed_line = parsed_lines
29//!     .next()
30//!     .expect("Should have at least one line")
31//!     .expect("Should parse successfully");
32//!
33//! let ParsedLine {
34//!     series,
35//!     field_set,
36//!     timestamp,
37//! } = parsed_line;
38//!
39//! assert_eq!(series.measurement, "cpu");
40//!
41//! let tags = series.tag_set.unwrap();
42//! assert_eq!(tags[0].0, "host");
43//! assert_eq!(tags[0].1, "A");
44//! assert_eq!(tags[1].0, "region");
45//! assert_eq!(tags[1].1, "west");
46//!
47//! let field = &field_set[0];
48//! assert_eq!(field.0, "usage_system");
49//! assert_eq!(field.1, FieldValue::I64(64));
50//!
51//! assert_eq!(timestamp, Some(1590488773254420000));
52//! ```
53//!
54//! [InfluxDB Line Protocol]: https://v2.docs.influxdata.com/v2.0/reference/syntax/line-protocol
55//! [Go implementation]: https://github.com/influxdata/influxdb/blob/217eddc87e14a79b01d0c22994fc139f530094a2/models/points_parser.go
56//! [InfluxDB IOx]: https://github.com/influxdata/influxdb_iox
57//! [nom]: https://crates.io/crates/nom
58
59// Note this crate is published as its own crate on crates.io but kept in this repository for
60// maintenance convenience.
61//
62// Thus this crate can't use workspace lints, so these lint configurations must be here.
63#![deny(rustdoc::broken_intra_doc_links, rustdoc::bare_urls, rust_2018_idioms)]
64#![warn(
65    missing_copy_implementations,
66    missing_debug_implementations,
67    clippy::explicit_iter_loop,
68    clippy::use_self,
69    clippy::clone_on_ref_ptr,
70    clippy::todo,
71    clippy::dbg_macro,
72    unused_crate_dependencies
73)]
74// DO NOT REMOVE these lint configurations; see note above!
75
76pub mod builder;
77pub use builder::LineProtocolBuilder;
78
79use fmt::Display;
80use log::debug;
81use nom::{
82    branch::alt,
83    bytes::complete::{tag, take_while1},
84    character::complete::digit1,
85    combinator::{map, opt, recognize},
86    multi::many0,
87    sequence::{preceded, separated_pair, terminated, tuple},
88};
89use smallvec::SmallVec;
90use snafu::{ResultExt, Snafu};
91use std::cmp::Ordering;
92use std::{
93    borrow::Cow,
94    char,
95    collections::{btree_map::Entry, BTreeMap},
96    fmt,
97    ops::Deref,
98};
99
100/// Parsing errors that describe how a particular line is invalid line protocol.
101#[derive(Debug, Snafu)]
102#[non_exhaustive]
103pub enum Error {
104    #[snafu(display(r#"Must not contain duplicate tags, but "{}" was repeated"#, tag_key))]
105    DuplicateTag { tag_key: String },
106
107    #[snafu(display(r#"Invalid measurement was provided"#))]
108    MeasurementValueInvalid,
109
110    #[snafu(display(r#"No fields were provided"#))]
111    FieldSetMissing,
112
113    #[snafu(display(r#"Unable to parse integer value '{}'"#, value))]
114    IntegerValueInvalid {
115        source: std::num::ParseIntError,
116        value: String,
117    },
118
119    #[snafu(display(r#"Unable to parse unsigned integer value '{}'"#, value))]
120    UIntegerValueInvalid {
121        source: std::num::ParseIntError,
122        value: String,
123    },
124
125    #[snafu(display(r#"Unable to parse floating-point value '{}'"#, value))]
126    FloatValueInvalid {
127        source: std::num::ParseFloatError,
128        value: String,
129    },
130
131    #[snafu(display(r#"Unable to parse timestamp value '{}'"#, value))]
132    TimestampValueInvalid {
133        source: std::num::ParseIntError,
134        value: String,
135    },
136
137    // This error is for compatibility with the Go parser
138    #[snafu(display(
139        r#"Measurements, tag keys and values, and field keys may not end with a backslash"#
140    ))]
141    EndsWithBackslash,
142
143    #[snafu(display(
144        "Could not parse entire line. Found trailing content: '{}'",
145        trailing_content
146    ))]
147    CannotParseEntireLine { trailing_content: String },
148
149    #[snafu(display(r#"Tag Set Malformed"#))]
150    TagSetMalformed,
151
152    // TODO: Replace this with specific failures.
153    #[snafu(display(r#"A generic parsing error occurred: {:?}"#, kind))]
154    GenericParsingError {
155        kind: nom::error::ErrorKind,
156        trace: Vec<Error>,
157    },
158}
159
160/// A specialized [`Result`] type with a default error type of [`Error`].
161///
162/// [`Result`]: std::result::Result
163pub type Result<T, E = Error> = std::result::Result<T, E>;
164type IResult<I, T, E = Error> = nom::IResult<I, T, E>;
165
166impl nom::error::ParseError<&str> for Error {
167    fn from_error_kind(_input: &str, kind: nom::error::ErrorKind) -> Self {
168        GenericParsingSnafu {
169            kind,
170            trace: vec![],
171        }
172        .build()
173    }
174
175    fn append(_input: &str, kind: nom::error::ErrorKind, other: Self) -> Self {
176        GenericParsingSnafu {
177            kind,
178            trace: vec![other],
179        }
180        .build()
181    }
182}
183
184/// Represents a single parsed line of line protocol data. See the [crate-level documentation](self)
185/// for more information and examples.
186#[derive(Debug)]
187pub struct ParsedLine<'a> {
188    pub series: Series<'a>,
189    pub field_set: FieldSet<'a>,
190    pub timestamp: Option<i64>,
191}
192
193impl<'a> ParsedLine<'a> {
194    /// Total number of columns in this line, including fields, tags, and
195    /// timestamp (timestamp is always present).
196    ///
197    /// ```
198    /// use influxdb_line_protocol::{ParsedLine, FieldValue};
199    ///
200    /// let mut parsed_lines =
201    ///     influxdb_line_protocol::parse_lines(
202    ///         "cpu,host=A,region=west usage_system=64i 1590488773254420000"
203    ///     );
204    /// let parsed_line = parsed_lines
205    ///     .next()
206    ///     .expect("Should have at least one line")
207    ///     .expect("Should parse successfully");
208    ///
209    /// assert_eq!(parsed_line.column_count(), 4);
210    /// ```
211    pub fn column_count(&self) -> usize {
212        1 + self.field_set.len() + self.series.tag_set.as_ref().map_or(0, |t| t.len())
213    }
214
215    /// Returns the value of the passed-in tag, if present.
216    pub fn tag_value(&self, tag_key: &str) -> Option<&EscapedStr<'a>> {
217        match &self.series.tag_set {
218            Some(t) => {
219                let t = t.iter().find(|(k, _)| *k == tag_key);
220                t.map(|(_, val)| val)
221            }
222            None => None,
223        }
224    }
225
226    /// Returns the value of the passed-in field, if present.
227    pub fn field_value(&self, field_key: &str) -> Option<&FieldValue<'a>> {
228        let f = self.field_set.iter().find(|(f, _)| *f == field_key);
229        f.map(|(_, val)| val)
230    }
231}
232
233/// Converts from a `ParsedLine` back to (canonical) line protocol
234///
235/// A note on validity: This code does not error or panic if the
236/// `ParsedLine` represents invalid line protocol (for example, if it
237/// has 0 fields).
238///
239/// Thus, if the `ParsedLine` represents invalid line protocol, then
240/// the result of `Display` / `to_string()` will also be invalid.
241impl<'a> Display for ParsedLine<'a> {
242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        write!(f, "{}", self.series)?;
244
245        if !self.field_set.is_empty() {
246            write!(f, " ")?;
247
248            let mut first = true;
249            for (field_name, field_value) in &self.field_set {
250                if !first {
251                    write!(f, ",")?;
252                }
253                first = false;
254                escape_and_write_value(f, field_name.as_str(), FIELD_KEY_DELIMITERS)?;
255                write!(f, "={field_value}")?;
256            }
257        }
258
259        if let Some(timestamp) = self.timestamp {
260            write!(f, " {timestamp}")?
261        }
262        Ok(())
263    }
264}
265
266/// Represents the identifier of a series (measurement, tagset) for
267/// line protocol data
268#[derive(Debug)]
269pub struct Series<'a> {
270    raw_input: &'a str,
271    pub measurement: Measurement<'a>,
272    pub tag_set: Option<TagSet<'a>>,
273}
274
275/// Converts `Series` back to line protocol
276impl<'a> Display for Series<'a> {
277    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278        escape_and_write_value(f, self.measurement.as_str(), MEASUREMENT_DELIMITERS)?;
279        if let Some(tag_set) = &self.tag_set {
280            write!(f, ",")?;
281            let mut first = true;
282            for (tag_name, tag_value) in tag_set {
283                if !first {
284                    write!(f, ",")?;
285                }
286                first = false;
287                escape_and_write_value(f, tag_name.as_str(), TAG_KEY_DELIMITERS)?;
288                write!(f, "=")?;
289                escape_and_write_value(f, tag_value.as_str(), TAG_VALUE_DELIMITERS)?;
290            }
291        }
292        Ok(())
293    }
294}
295
296impl<'a> Series<'a> {
297    pub fn generate_base(self) -> Result<Cow<'a, str>> {
298        match (!self.is_escaped(), self.is_sorted_and_unique()) {
299            (true, true) => Ok(self.raw_input.into()),
300            (_, true) => Ok(self.generate_base_with_escaping().into()),
301            (_, _) => self
302                .generate_base_with_escaping_sorting_deduplicating()
303                .map(Into::into),
304        }
305    }
306
307    fn generate_base_with_escaping(self) -> String {
308        let mut series_base = self.measurement.to_string();
309        for (tag_key, tag_value) in self.tag_set.unwrap_or_default() {
310            use std::fmt::Write;
311            write!(&mut series_base, ",{tag_key}={tag_value}").expect("Could not append string");
312        }
313        series_base
314    }
315
316    fn generate_base_with_escaping_sorting_deduplicating(self) -> Result<String> {
317        let mut unique_sorted_tag_set = BTreeMap::new();
318        for (tag_key, tag_value) in self.tag_set.unwrap_or_default() {
319            match unique_sorted_tag_set.entry(tag_key) {
320                Entry::Vacant(e) => {
321                    e.insert(tag_value);
322                }
323                Entry::Occupied(e) => {
324                    let (tag_key, _) = e.remove_entry();
325                    return DuplicateTagSnafu {
326                        tag_key: tag_key.to_string(),
327                    }
328                    .fail();
329                }
330            }
331        }
332
333        let mut series_base = self.measurement.to_string();
334        for (tag_key, tag_value) in unique_sorted_tag_set {
335            use std::fmt::Write;
336            write!(&mut series_base, ",{tag_key}={tag_value}").expect("Could not append string");
337        }
338
339        Ok(series_base)
340    }
341
342    fn is_escaped(&self) -> bool {
343        self.measurement.is_escaped() || {
344            match &self.tag_set {
345                None => false,
346                Some(tag_set) => tag_set
347                    .iter()
348                    .any(|(tag_key, tag_value)| tag_key.is_escaped() || tag_value.is_escaped()),
349            }
350        }
351    }
352
353    fn is_sorted_and_unique(&self) -> bool {
354        match &self.tag_set {
355            None => true,
356            Some(tag_set) => {
357                let mut i = tag_set.iter().zip(tag_set.iter().skip(1));
358                i.all(|((last_tag_key, _), (this_tag_key, _))| last_tag_key < this_tag_key)
359            }
360        }
361    }
362}
363
364pub type Measurement<'a> = EscapedStr<'a>;
365
366/// The [field] keys and values that appear in the line of line protocol.
367///
368/// [field]: https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/#field-set
369pub type FieldSet<'a> = SmallVec<[(EscapedStr<'a>, FieldValue<'a>); 4]>;
370
371/// The [tag] keys and values that appear in the line of line protocol.
372///
373/// [tag]: https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/#tag-set
374pub type TagSet<'a> = SmallVec<[(EscapedStr<'a>, EscapedStr<'a>); 8]>;
375
376/// Allowed types of fields in a `ParsedLine`. One of the types described in [the line protocol
377/// reference].
378///
379/// [the line protocol reference]: https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/#data-types-and-format
380#[derive(Debug, Clone, PartialEq)]
381pub enum FieldValue<'a> {
382    I64(i64),
383    U64(u64),
384    F64(f64),
385    String(EscapedStr<'a>),
386    Boolean(bool),
387}
388
389impl<'a> FieldValue<'a> {
390    /// Returns true if `self` and `other` are of the same data type.
391    pub fn is_same_type(&self, other: &Self) -> bool {
392        std::mem::discriminant(self) == std::mem::discriminant(other)
393    }
394}
395
396/// Converts `FieldValue` back to line protocol.
397/// See [the line protocol reference] for more detail.
398///
399/// [the line protocol reference]: https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/#data-types-and-format
400impl<'a> Display for FieldValue<'a> {
401    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
402        match self {
403            Self::I64(v) => write!(f, "{v}i"),
404            Self::U64(v) => write!(f, "{v}u"),
405            Self::F64(v) => write!(f, "{v}"),
406            Self::String(v) => escape_and_write_value(f, v, FIELD_VALUE_STRING_DELIMITERS),
407            Self::Boolean(v) => write!(f, "{v}"),
408        }
409    }
410}
411
412/// Represents a single logical string in the input.
413///
414/// We do not use `&str` directly here because the actual input may be
415/// escaped, in which case the data in the input buffer is not
416/// contiguous. This enum provides an interface to access all such
417/// strings as contiguous string slices for compatibility with other
418/// code, and is optimized for the common case where the
419/// input is all in a contiguous string slice.
420///
421/// For example, the 8-character string `Foo\\Bar` (note the double
422/// `\\`) is parsed into the logical 7-character string `Foo\Bar`
423/// (note the single `\`)
424#[derive(Debug, Clone, Eq, Hash)]
425pub enum EscapedStr<'a> {
426    SingleSlice(&'a str),
427    CopiedValue(String),
428}
429
430impl fmt::Display for EscapedStr<'_> {
431    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432        match self {
433            EscapedStr::SingleSlice(s) => s.fmt(f)?,
434            EscapedStr::CopiedValue(s) => s.fmt(f)?,
435        }
436        Ok(())
437    }
438}
439
440impl<'a> EscapedStr<'a> {
441    fn from_slices(v: &[&'a str]) -> EscapedStr<'a> {
442        match v.len() {
443            0 => EscapedStr::SingleSlice(""),
444            1 => EscapedStr::SingleSlice(v[0]),
445            _ => EscapedStr::CopiedValue(v.join("")),
446        }
447    }
448
449    fn is_escaped(&self) -> bool {
450        match self {
451            EscapedStr::SingleSlice(_) => false,
452            EscapedStr::CopiedValue(_) => true,
453        }
454    }
455
456    /// Return the logical representation for the `EscapedStr` as a
457    /// single slice. The slice might not point into the original
458    /// buffer.
459    pub fn as_str(&self) -> &str {
460        self
461    }
462}
463
464impl<'a> Deref for EscapedStr<'a> {
465    type Target = str;
466
467    fn deref(&self) -> &Self::Target {
468        match &self {
469            EscapedStr::SingleSlice(s) => s,
470            EscapedStr::CopiedValue(s) => s,
471        }
472    }
473}
474
475impl<'a> PartialEq for EscapedStr<'a> {
476    fn eq(&self, other: &Self) -> bool {
477        self.as_str() == other.as_str()
478    }
479}
480
481impl<'a> PartialOrd for EscapedStr<'a> {
482    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
483        Some(self.cmp(other))
484    }
485}
486
487impl<'a> Ord for EscapedStr<'a> {
488    fn cmp(&self, other: &Self) -> Ordering {
489        self.as_str().cmp(other.as_str())
490    }
491}
492
493impl<'a> From<&'a str> for EscapedStr<'a> {
494    fn from(other: &'a str) -> Self {
495        EscapedStr::SingleSlice(other)
496    }
497}
498
499impl From<EscapedStr<'_>> for String {
500    fn from(other: EscapedStr<'_>) -> Self {
501        match other {
502            EscapedStr::SingleSlice(s) => s.into(),
503            EscapedStr::CopiedValue(s) => s,
504        }
505    }
506}
507
508impl From<&EscapedStr<'_>> for String {
509    fn from(other: &EscapedStr<'_>) -> Self {
510        other.to_string()
511    }
512}
513
514impl PartialEq<&str> for EscapedStr<'_> {
515    fn eq(&self, other: &&str) -> bool {
516        self.as_str() == *other
517    }
518}
519
520impl PartialEq<String> for EscapedStr<'_> {
521    fn eq(&self, other: &String) -> bool {
522        self.as_str() == other
523    }
524}
525
526/// Parses a new line-delimited string into an iterator of
527/// [`ParsedLine`]. See the [crate-level documentation](self) for more
528/// information and examples.
529pub fn parse_lines(input: &str) -> impl Iterator<Item = Result<ParsedLine<'_>>> {
530    split_lines(input).filter_map(|line| {
531        let i = trim_leading(line);
532
533        if i.is_empty() {
534            return None;
535        }
536
537        let res = match parse_line(i) {
538            Ok((remaining, line)) => {
539                // should have parsed the whole input line; if any
540                // data remains it is a parse error for this line.
541                // Corresponding Go logic:
542                // https://github.com/influxdata/influxdb/blob/217eddc87e14a79b01d0c22994fc139f530094a2/models/points_parser.go#L259-L266
543                if !remaining.is_empty() {
544                    Some(Err(Error::CannotParseEntireLine {
545                        trailing_content: String::from(remaining),
546                    }))
547                } else {
548                    Some(Ok(line))
549                }
550            }
551            Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => Some(Err(e)),
552            Err(nom::Err::Incomplete(_)) => unreachable!("Cannot have incomplete data"), // Only streaming parsers have this
553        };
554
555        if let Some(Err(r)) = &res {
556            debug!("Error parsing line: '{}'. Error was {:?}", line, r);
557        }
558        res
559    })
560}
561
562/// Split `input` into individual lines to be parsed, based on the
563/// rules of the line protocol format.
564///
565/// This code is more or less a direct port of the [Go implementation of
566/// `scanLine`](https://github.com/influxdata/influxdb/blob/217eddc87e14a79b01d0c22994fc139f530094a2/models/points.go#L1078)
567///
568/// While this choice of implementation definitely means there is
569/// logic duplication for scanning fields, duplicating it also means
570/// we can be more sure of the compatibility of the Rust parser and
571/// the canonical Go parser.
572pub fn split_lines(input: &str) -> impl Iterator<Item = &str> {
573    // NB: This is ported as closely as possible from the original Go code:
574    let mut quoted = false;
575    let mut fields = false;
576
577    // tracks how many '=' and commas we've seen
578    // this duplicates some of the functionality in scanFields
579    let mut equals = 0;
580    let mut commas = 0;
581
582    let mut in_escape = false;
583    input.split(move |c| {
584        // skip past escaped characters
585        if in_escape {
586            in_escape = false;
587            return false;
588        }
589
590        if c == '\\' {
591            in_escape = true;
592            return false;
593        }
594
595        if c == ' ' {
596            fields = true;
597            return false;
598        }
599
600        // If we see a double quote, makes sure it is not escaped
601        if fields {
602            if !quoted && c == '=' {
603                equals += 1;
604                return false;
605            } else if !quoted && c == ',' {
606                commas += 1;
607                return false;
608            } else if c == '"' && equals > commas {
609                quoted = !quoted;
610                return false;
611            }
612        }
613
614        if c == '\n' && !quoted {
615            // reset all the state -- we found a line
616            quoted = false;
617            fields = false;
618            equals = 0;
619            commas = 0;
620            assert!(!in_escape);
621            in_escape = false;
622            return true;
623        }
624
625        false
626    })
627}
628
629fn parse_line(i: &str) -> IResult<&str, ParsedLine<'_>> {
630    let field_set = preceded(whitespace, field_set);
631    let timestamp = preceded(whitespace, terminated(timestamp, opt(whitespace)));
632
633    let line = tuple((series, field_set, opt(timestamp)));
634
635    map(line, |(series, field_set, timestamp)| ParsedLine {
636        series,
637        field_set,
638        timestamp,
639    })(i)
640}
641
642fn series(i: &str) -> IResult<&str, Series<'_>> {
643    let series = tuple((measurement, maybe_tagset));
644    let series_and_raw_input = parse_and_recognize(series);
645
646    map(
647        series_and_raw_input,
648        |(raw_input, (measurement, tag_set))| Series {
649            raw_input,
650            measurement,
651            tag_set,
652        },
653    )(i)
654}
655
656/// Tagsets are optional, but if a comma follows the measurement, then we must have at least one tag=value pair.
657/// anything else is an error
658fn maybe_tagset(i: &str) -> IResult<&str, Option<TagSet<'_>>, Error> {
659    match tag::<&str, &str, Error>(",")(i) {
660        Err(nom::Err::Error(_)) => Ok((i, None)),
661        Ok((remainder, _)) => {
662            match tag_set(remainder) {
663                Ok((i, ts)) => {
664                    // reaching here, we must find a tagset, which is at least one tag=value pair.
665                    if ts.is_empty() {
666                        return Err(nom::Err::Error(Error::TagSetMalformed));
667                    }
668                    Ok((i, Some(ts)))
669                }
670                Err(nom::Err::Error(_)) => TagSetMalformedSnafu.fail().map_err(nom::Err::Error),
671                Err(e) => Err(e),
672            }
673        }
674        Err(e) => Err(e),
675    }
676}
677
678fn measurement(i: &str) -> IResult<&str, Measurement<'_>, Error> {
679    let normal_char = take_while1(|c| {
680        !is_whitespace_boundary_char(c) && !is_null_char(c) && c != ',' && c != '\\'
681    });
682
683    let space = map(tag(" "), |_| " ");
684    let comma = map(tag(","), |_| ",");
685    let backslash = map(tag("\\"), |_| "\\");
686
687    let escaped = alt((comma, space, backslash));
688
689    match escape_or_fallback(normal_char, "\\", escaped)(i) {
690        Err(nom::Err::Error(_)) => MeasurementValueInvalidSnafu.fail().map_err(nom::Err::Error),
691        other => other,
692    }
693}
694
695fn tag_set(i: &str) -> IResult<&str, TagSet<'_>> {
696    let one_tag = separated_pair(tag_key, tag("="), tag_value);
697    parameterized_separated_list(tag(","), one_tag, SmallVec::new, |v, i| v.push(i))(i)
698}
699
700fn tag_key(i: &str) -> IResult<&str, EscapedStr<'_>> {
701    let normal_char = take_while1(|c| !is_whitespace_boundary_char(c) && c != '=' && c != '\\');
702
703    escaped_value(normal_char)(i)
704}
705
706fn tag_value(i: &str) -> IResult<&str, EscapedStr<'_>> {
707    let normal_char = take_while1(|c| !is_whitespace_boundary_char(c) && c != ',' && c != '\\');
708    escaped_value(normal_char)(i)
709}
710
711fn field_set(i: &str) -> IResult<&str, FieldSet<'_>> {
712    let one_field = separated_pair(field_key, tag("="), field_value);
713    let sep = tag(",");
714
715    match parameterized_separated_list1(sep, one_field, SmallVec::new, |v, i| v.push(i))(i) {
716        Err(nom::Err::Error(_)) => FieldSetMissingSnafu.fail().map_err(nom::Err::Error),
717        other => other,
718    }
719}
720
721fn field_key(i: &str) -> IResult<&str, EscapedStr<'_>> {
722    let normal_char = take_while1(|c| !is_whitespace_boundary_char(c) && c != '=' && c != '\\');
723    escaped_value(normal_char)(i)
724}
725
726fn field_value(i: &str) -> IResult<&str, FieldValue<'_>> {
727    let int = map(field_integer_value, FieldValue::I64);
728    let uint = map(field_uinteger_value, FieldValue::U64);
729    let float = map(field_float_value, FieldValue::F64);
730    let string = map(field_string_value, FieldValue::String);
731    let boolv = map(field_bool_value, FieldValue::Boolean);
732
733    alt((int, uint, float, string, boolv))(i)
734}
735
736fn field_integer_value(i: &str) -> IResult<&str, i64> {
737    let tagged_value = terminated(integral_value_signed, tag("i"));
738    map_fail(tagged_value, |value| {
739        value.parse().context(IntegerValueInvalidSnafu { value })
740    })(i)
741}
742
743fn field_uinteger_value(i: &str) -> IResult<&str, u64> {
744    let tagged_value = terminated(digit1, tag("u"));
745    map_fail(tagged_value, |value| {
746        value.parse().context(UIntegerValueInvalidSnafu { value })
747    })(i)
748}
749
750fn field_float_value(i: &str) -> IResult<&str, f64> {
751    let value = alt((
752        field_float_value_with_exponential_and_decimal,
753        field_float_value_with_exponential_no_decimal,
754        field_float_value_with_decimal,
755        field_float_value_no_decimal,
756    ));
757    map_fail(value, |value| {
758        value.parse().context(FloatValueInvalidSnafu { value })
759    })(i)
760}
761
762fn field_float_value_with_decimal(i: &str) -> IResult<&str, &str> {
763    recognize(separated_pair(integral_value_signed, tag("."), digit1))(i)
764}
765
766fn field_float_value_with_exponential_and_decimal(i: &str) -> IResult<&str, &str> {
767    recognize(separated_pair(
768        integral_value_signed,
769        tag("."),
770        exponential_value,
771    ))(i)
772}
773
774fn field_float_value_with_exponential_no_decimal(i: &str) -> IResult<&str, &str> {
775    recognize(preceded(opt(tag("-")), exponential_value))(i)
776}
777
778fn exponential_value(i: &str) -> IResult<&str, &str> {
779    recognize(separated_pair(
780        digit1,
781        tuple((alt((tag("e"), tag("E"))), opt(alt((tag("-"), tag("+")))))),
782        digit1,
783    ))(i)
784}
785
786fn field_float_value_no_decimal(i: &str) -> IResult<&str, &str> {
787    integral_value_signed(i)
788}
789
790fn integral_value_signed(i: &str) -> IResult<&str, &str> {
791    recognize(preceded(opt(tag("-")), digit1))(i)
792}
793
794fn timestamp(i: &str) -> IResult<&str, i64> {
795    map_fail(integral_value_signed, |value| {
796        value.parse().context(TimestampValueInvalidSnafu { value })
797    })(i)
798}
799
800fn field_string_value(i: &str) -> IResult<&str, EscapedStr<'_>> {
801    // https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/#data-types-and-format
802    // For string field values, backslash is only used to escape itself (`\`) or double
803    // quotes.
804    let string_data = alt((
805        map(tag(r#"\""#), |_| r#"""#), // escaped double quote -> double quote
806        map(tag(r"\\"), |_| r"\"),     // escaped backslash --> single backslash
807        tag(r"\"),                     // unescaped single backslash
808        take_while1(|c| c != '\\' && c != '"'), // anything else w/ no special handling
809    ));
810
811    // NB: `many0` doesn't allow combinators that match the empty string so
812    // we need to special case a pair of double quotes.
813    let empty_str = map(tag(r#""""#), |_| Vec::new());
814
815    let quoted_str = alt((
816        preceded(tag("\""), terminated(many0(string_data), tag("\""))),
817        empty_str,
818    ));
819
820    map(quoted_str, |vec| EscapedStr::from_slices(&vec))(i)
821}
822
823fn field_bool_value(i: &str) -> IResult<&str, bool> {
824    // https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/#data-types-and-format
825    // "specify TRUE with t, T, true, True, or TRUE. Specify FALSE with f, F, false,
826    // False, or FALSE"
827    alt((
828        map(tag("true"), |_| true),
829        map(tag("True"), |_| true),
830        map(tag("TRUE"), |_| true),
831        map(tag("t"), |_| true),
832        map(tag("T"), |_| true),
833        map(tag("false"), |_| false),
834        map(tag("False"), |_| false),
835        map(tag("FALSE"), |_| false),
836        map(tag("f"), |_| false),
837        map(tag("F"), |_| false),
838    ))(i)
839}
840
841/// Truncates the input slice to remove all whitespace from the
842/// beginning (left), including completely commented-out lines
843fn trim_leading(mut i: &str) -> &str {
844    loop {
845        let offset = i
846            .find(|c| !is_whitespace_boundary_char(c))
847            .unwrap_or(i.len());
848        i = &i[offset..];
849
850        if i.starts_with('#') {
851            let offset = i.find('\n').unwrap_or(i.len());
852            i = &i[offset..];
853        } else {
854            break i;
855        }
856    }
857}
858
859fn whitespace(i: &str) -> IResult<&str, &str> {
860    take_while1(|c| c == ' ')(i)
861}
862
863fn is_whitespace_boundary_char(c: char) -> bool {
864    c == ' ' || c == '\t' || c == '\n'
865}
866
867fn is_null_char(c: char) -> bool {
868    c == '\0'
869}
870
871/// While not all of these escape characters are required to be
872/// escaped, we support the client escaping them proactively to
873/// provide a common experience.
874fn escaped_value<'a>(
875    normal: impl Fn(&'a str) -> IResult<&'a str, &'a str>,
876) -> impl FnOnce(&'a str) -> IResult<&'a str, EscapedStr<'a>> {
877    move |i| {
878        let backslash = map(tag("\\"), |_| "\\");
879        let comma = map(tag(","), |_| ",");
880        let equal = map(tag("="), |_| "=");
881        let space = map(tag(" "), |_| " ");
882
883        let escaped = alt((backslash, comma, equal, space));
884
885        escape_or_fallback(normal, "\\", escaped)(i)
886    }
887}
888
889/// Parse an unescaped piece of text, interspersed with
890/// potentially-escaped characters. If the character *isn't* escaped,
891/// treat it as a literal character.
892fn escape_or_fallback<'a>(
893    normal: impl FnMut(&'a str) -> IResult<&'a str, &'a str>,
894    escape_char: &'static str,
895    escaped: impl FnMut(&'a str) -> IResult<&'a str, &'a str>,
896) -> impl FnOnce(&'a str) -> IResult<&'a str, EscapedStr<'a>> {
897    move |i| {
898        let (remaining, s) = escape_or_fallback_inner(normal, escape_char, escaped)(i)?;
899
900        if s.ends_with('\\') {
901            EndsWithBackslashSnafu.fail().map_err(nom::Err::Failure)
902        } else {
903            Ok((remaining, s))
904        }
905    }
906}
907
908fn escape_or_fallback_inner<'a, Error>(
909    mut normal: impl FnMut(&'a str) -> IResult<&'a str, &'a str, Error>,
910    escape_char: &'static str,
911    mut escaped: impl FnMut(&'a str) -> IResult<&'a str, &'a str, Error>,
912) -> impl FnMut(&'a str) -> IResult<&'a str, EscapedStr<'a>, Error>
913where
914    Error: nom::error::ParseError<&'a str>,
915{
916    move |i| {
917        let mut result = SmallVec::<[&str; 4]>::new();
918        let mut head = i;
919
920        loop {
921            match normal(head) {
922                Ok((remaining, parsed)) => {
923                    result.push(parsed);
924                    head = remaining;
925                }
926                Err(nom::Err::Error(_)) => {
927                    // FUTURE: https://doc.rust-lang.org/std/primitive.str.html#method.strip_prefix
928                    if head.starts_with(escape_char) {
929                        let after = &head[escape_char.len()..];
930
931                        match escaped(after) {
932                            Ok((remaining, parsed)) => {
933                                result.push(parsed);
934                                head = remaining;
935                            }
936                            Err(nom::Err::Error(_)) => {
937                                result.push(escape_char);
938                                head = after;
939
940                                // The Go parser assumes that *any* unknown escaped character is
941                                // valid.
942                                match head.chars().next() {
943                                    Some(c) => {
944                                        let (escaped, remaining) = head.split_at(c.len_utf8());
945                                        result.push(escaped);
946                                        head = remaining;
947                                    }
948                                    None => return Ok((head, EscapedStr::from_slices(&result))),
949                                }
950                            }
951                            Err(e) => return Err(e),
952                        }
953                    } else {
954                        // have we parsed *anything*?
955                        if head == i {
956                            return Err(nom::Err::Error(Error::from_error_kind(
957                                head,
958                                nom::error::ErrorKind::EscapedTransform,
959                            )));
960                        } else {
961                            return Ok((head, EscapedStr::from_slices(&result)));
962                        }
963                    }
964                }
965                Err(e) => return Err(e),
966            }
967        }
968    }
969}
970
971/// This is a copied version of nom's `separated_list` that allows
972/// parameterizing the created collection via closures.
973fn parameterized_separated_list<I, O, O2, E, F, G, Ret>(
974    mut sep: G,
975    mut f: F,
976    cre: impl FnOnce() -> Ret,
977    mut add: impl FnMut(&mut Ret, O),
978) -> impl FnOnce(I) -> IResult<I, Ret, E>
979where
980    I: Clone + PartialEq,
981    F: FnMut(I) -> IResult<I, O, E>,
982    G: FnMut(I) -> IResult<I, O2, E>,
983    E: nom::error::ParseError<I>,
984{
985    move |mut i: I| {
986        let mut res = cre();
987
988        match f(i.clone()) {
989            Err(nom::Err::Error(_)) => return Ok((i, res)),
990            Err(e) => return Err(e),
991            Ok((i1, o)) => {
992                if i1 == i {
993                    return Err(nom::Err::Error(E::from_error_kind(
994                        i1,
995                        nom::error::ErrorKind::SeparatedList,
996                    )));
997                }
998
999                add(&mut res, o);
1000                i = i1;
1001            }
1002        }
1003
1004        loop {
1005            match sep(i.clone()) {
1006                Err(nom::Err::Error(_)) => return Ok((i, res)),
1007                Err(e) => return Err(e),
1008                Ok((i1, _)) => {
1009                    if i1 == i {
1010                        return Err(nom::Err::Error(E::from_error_kind(
1011                            i1,
1012                            nom::error::ErrorKind::SeparatedList,
1013                        )));
1014                    }
1015
1016                    match f(i1.clone()) {
1017                        Err(nom::Err::Error(_)) => return Ok((i, res)),
1018                        Err(e) => return Err(e),
1019                        Ok((i2, o)) => {
1020                            if i2 == i {
1021                                return Err(nom::Err::Error(E::from_error_kind(
1022                                    i2,
1023                                    nom::error::ErrorKind::SeparatedList,
1024                                )));
1025                            }
1026
1027                            add(&mut res, o);
1028                            i = i2;
1029                        }
1030                    }
1031                }
1032            }
1033        }
1034    }
1035}
1036
1037fn parameterized_separated_list1<I, O, O2, E, F, G, Ret>(
1038    mut sep: G,
1039    mut f: F,
1040    cre: impl FnOnce() -> Ret,
1041    mut add: impl FnMut(&mut Ret, O),
1042) -> impl FnOnce(I) -> IResult<I, Ret, E>
1043where
1044    I: Clone + PartialEq,
1045    F: FnMut(I) -> IResult<I, O, E>,
1046    G: FnMut(I) -> IResult<I, O2, E>,
1047    E: nom::error::ParseError<I>,
1048{
1049    move |i| {
1050        let (rem, first) = f(i)?;
1051
1052        let mut res = cre();
1053        add(&mut res, first);
1054
1055        match sep(rem.clone()) {
1056            Ok((rem, _)) => parameterized_separated_list(sep, f, move || res, add)(rem),
1057            Err(nom::Err::Error(_)) => Ok((rem, res)),
1058            Err(e) => Err(e),
1059        }
1060    }
1061}
1062
1063/// This is a copied version of nom's `recognize` that runs the parser
1064/// **and** returns the entire matched input.
1065fn parse_and_recognize<
1066    I: Clone + nom::Offset + nom::Slice<std::ops::RangeTo<usize>>,
1067    O,
1068    E: nom::error::ParseError<I>,
1069    F,
1070>(
1071    mut parser: F,
1072) -> impl FnMut(I) -> IResult<I, (I, O), E>
1073where
1074    F: FnMut(I) -> IResult<I, O, E>,
1075{
1076    move |input: I| {
1077        let i = input.clone();
1078        match parser(i) {
1079            Ok((i, o)) => {
1080                let index = input.offset(&i);
1081                Ok((i, (input.slice(..index), o)))
1082            }
1083            Err(e) => Err(e),
1084        }
1085    }
1086}
1087
1088/// This is very similar to nom's `map_res`, but creates a
1089/// `nom::Err::Failure` instead.
1090fn map_fail<'a, R1, R2>(
1091    mut first: impl FnMut(&'a str) -> IResult<&'a str, R1>,
1092    second: impl FnOnce(R1) -> Result<R2, Error>,
1093) -> impl FnOnce(&'a str) -> IResult<&'a str, R2> {
1094    move |i| {
1095        let (remaining, value) = first(i)?;
1096
1097        match second(value) {
1098            Ok(v) => Ok((remaining, v)),
1099            Err(e) => Err(nom::Err::Failure(e)),
1100        }
1101    }
1102}
1103
1104// copy / pasted from influxdb2_client to avoid a dependency on that crate
1105
1106/// Characters to escape when writing measurement names
1107const MEASUREMENT_DELIMITERS: &[char] = &[',', ' '];
1108
1109/// Characters to escape when writing tag keys
1110const TAG_KEY_DELIMITERS: &[char] = &[',', '=', ' '];
1111
1112/// Characters to escape when writing tag values
1113const TAG_VALUE_DELIMITERS: &[char] = TAG_KEY_DELIMITERS;
1114
1115/// Characters to escape when writing field keys
1116const FIELD_KEY_DELIMITERS: &[char] = TAG_KEY_DELIMITERS;
1117
1118/// Characters to escape when writing string values in fields
1119const FIELD_VALUE_STRING_DELIMITERS: &[char] = &['"']; // " Close quotes for buggy editor
1120
1121/// Writes a `&str` value to `f`, escaping all characters in
1122/// `escaping_specificiation`.
1123///
1124/// Use the constants defined in this module.
1125fn escape_and_write_value(
1126    f: &mut fmt::Formatter<'_>,
1127    value: &str,
1128    escaping_specification: &[char],
1129) -> fmt::Result {
1130    let mut last = 0;
1131
1132    for (idx, delim) in value.match_indices(escaping_specification) {
1133        let s = &value[last..idx];
1134        write!(f, r#"{s}\{delim}"#)?;
1135        last = idx + delim.len();
1136    }
1137
1138    f.write_str(&value[last..])
1139}
1140
1141#[cfg(test)]
1142mod test {
1143    use super::*;
1144    use smallvec::smallvec;
1145    use test_helpers::approximately_equal;
1146
1147    impl FieldValue<'_> {
1148        fn unwrap_i64(&self) -> i64 {
1149            match self {
1150                Self::I64(v) => *v,
1151                _ => panic!("field was not an i64"),
1152            }
1153        }
1154
1155        fn unwrap_u64(&self) -> u64 {
1156            match self {
1157                Self::U64(v) => *v,
1158                _ => panic!("field was not an u64"),
1159            }
1160        }
1161
1162        fn unwrap_f64(&self) -> f64 {
1163            match self {
1164                Self::F64(v) => *v,
1165                _ => panic!("field was not an f64"),
1166            }
1167        }
1168
1169        fn unwrap_string(&self) -> String {
1170            match self {
1171                Self::String(v) => v.to_string(),
1172                _ => panic!("field was not a String"),
1173            }
1174        }
1175
1176        fn unwrap_bool(&self) -> bool {
1177            match self {
1178                Self::Boolean(v) => *v,
1179                _ => panic!("field was not a Bool"),
1180            }
1181        }
1182    }
1183
1184    #[test]
1185    fn parse_lines_returns_all_lines_even_when_a_line_errors() {
1186        let input = ",tag1=1,tag2=2 value=1 123\nm,tag1=one,tag2=2 value=1 123";
1187        let vals = super::parse_lines(input).collect::<Vec<Result<_>>>();
1188        assert!(matches!(
1189            &vals[..],
1190            &[Err(Error::MeasurementValueInvalid), Ok(_)]
1191        ));
1192    }
1193
1194    #[test]
1195    fn escaped_str_basic() {
1196        // Demonstrate how strings without any escapes are handled.
1197        let es = EscapedStr::from("Foo");
1198        assert_eq!(es, "Foo");
1199        assert!(!es.is_escaped(), "There are no escaped values");
1200        assert!(!es.ends_with('F'));
1201        assert!(!es.ends_with('z'));
1202        assert!(!es.ends_with("zz"));
1203        assert!(es.ends_with('o'));
1204        assert!(es.ends_with("oo"));
1205        assert!(es.ends_with("Foo"));
1206    }
1207
1208    #[test]
1209    fn escaped_str_multi() {
1210        // Get an EscapedStr that has multiple parts by parsing a
1211        // measurement name with a non-whitespace escape character
1212        let (remaining, es) = measurement("Foo\\aBar").unwrap();
1213        assert!(remaining.is_empty());
1214        assert_eq!(es, EscapedStr::from_slices(&["Foo", "\\", "a", "Bar"]));
1215        assert!(es.is_escaped());
1216
1217        // Test `ends_with` across boundaries
1218        assert!(es.ends_with("Bar"));
1219
1220        // Test PartialEq implementation for escaped str
1221        assert!(es == "Foo\\aBar");
1222        assert!(es != "Foo\\aBa");
1223        assert!(es != "Foo\\aBaz");
1224        assert!(es != "Foo\\a");
1225        assert!(es != "Foo\\");
1226        assert!(es != "Foo");
1227        assert!(es != "Fo");
1228        assert!(es != "F");
1229        assert!(es != "");
1230    }
1231
1232    #[test]
1233    fn test_trim_leading() {
1234        assert_eq!(trim_leading(""), "");
1235        assert_eq!(trim_leading("  a b c "), "a b c ");
1236        assert_eq!(trim_leading("  a "), "a ");
1237        assert_eq!(trim_leading("\n  a "), "a ");
1238        assert_eq!(trim_leading("\t  a "), "a ");
1239
1240        // comments
1241        assert_eq!(trim_leading("  #comment\n a "), "a ");
1242        assert_eq!(trim_leading("#comment\tcomment"), "");
1243        assert_eq!(trim_leading("#comment\n #comment2\n#comment\na"), "a");
1244    }
1245
1246    #[test]
1247    fn test_split_lines() {
1248        assert_eq!(split_lines("").collect::<Vec<_>>(), vec![""]);
1249        assert_eq!(split_lines("foo").collect::<Vec<_>>(), vec!["foo"]);
1250        assert_eq!(
1251            split_lines("foo\nbar").collect::<Vec<_>>(),
1252            vec!["foo", "bar"]
1253        );
1254        assert_eq!(
1255            split_lines("foo\nbar\nbaz").collect::<Vec<_>>(),
1256            vec!["foo", "bar", "baz"]
1257        );
1258
1259        assert_eq!(
1260            split_lines("foo\\nbar\nbaz").collect::<Vec<_>>(),
1261            vec!["foo\\nbar", "baz"]
1262        );
1263        assert_eq!(
1264            split_lines("meas tag=val field=1\nnext\n").collect::<Vec<_>>(),
1265            vec!["meas tag=val field=1", "next", ""]
1266        );
1267        assert_eq!(
1268            split_lines("meas tag=val field=\"\nval\"\nnext").collect::<Vec<_>>(),
1269            vec!["meas tag=val field=\"\nval\"", "next"]
1270        );
1271        assert_eq!(
1272            split_lines("meas tag=val field=\\\"\nval\"\nnext").collect::<Vec<_>>(),
1273            vec!["meas tag=val field=\\\"", "val\"", "next"]
1274        );
1275        assert_eq!(
1276            split_lines("meas tag=val field=1,field=\"\nval\"\nnext").collect::<Vec<_>>(),
1277            vec!["meas tag=val field=1,field=\"\nval\"", "next"]
1278        );
1279        assert_eq!(
1280            split_lines("meas tag=val field=1,field=\\\"\nval\"\nnext").collect::<Vec<_>>(),
1281            vec!["meas tag=val field=1,field=\\\"", "val\"", "next"]
1282        );
1283    }
1284
1285    #[test]
1286    fn escaped_str_multi_to_string() {
1287        let (_, es) = measurement("Foo\\aBar").unwrap();
1288        // test the From<> implementation
1289        assert_eq!(es, "Foo\\aBar");
1290    }
1291
1292    fn parse(s: &str) -> Result<Vec<ParsedLine<'_>>, super::Error> {
1293        super::parse_lines(s).collect()
1294    }
1295
1296    #[test]
1297    fn parse_empty() {
1298        let input = "";
1299        let vals = parse(input);
1300        assert_eq!(vals.unwrap().len(), 0);
1301    }
1302
1303    // tests that an incomplete tag=value pair returns an error about a malformed tagset
1304    #[test]
1305    fn parse_tag_no_value() {
1306        let input = "testmeasure,foo= bar=1i";
1307        let vals = parse(input);
1308        assert!(matches!(vals, Err(Error::TagSetMalformed)));
1309    }
1310
1311    // tests that just a comma after the measurement is an error
1312    #[test]
1313    fn parse_no_tagset() {
1314        let input = "testmeasure, bar=1i";
1315        let vals = parse(input);
1316        assert!(matches!(vals, Err(Error::TagSetMalformed)));
1317    }
1318
1319    #[test]
1320    fn parse_no_measurement() {
1321        let input = ",tag1=1,tag2=2 value=1 123";
1322        let vals = parse(input);
1323        assert!(matches!(vals, Err(Error::MeasurementValueInvalid)));
1324
1325        // accepts `field=1` as measurement, and errors on missing field
1326        let input = "field=1 1234";
1327        let vals = parse(input);
1328        assert!(matches!(vals, Err(Error::FieldSetMissing)));
1329    }
1330
1331    // matches behavior in influxdb golang parser
1332    #[test]
1333    fn parse_measurement_with_eq() {
1334        let input = "tag1=1 field=1 1234";
1335        let vals = parse(input);
1336        assert!(vals.is_ok());
1337
1338        let input = "tag1=1,tag2=2 value=1 123";
1339        let vals = parse(input);
1340        assert!(vals.is_ok());
1341    }
1342
1343    #[test]
1344    fn parse_null_measurement() {
1345        let input = "\0 field=1 1234";
1346        let vals = parse(input);
1347        assert!(matches!(vals, Err(Error::MeasurementValueInvalid)));
1348
1349        let input = "\0,tag1=1,tag2=2 value=1 123";
1350        let vals = parse(input);
1351        assert!(matches!(vals, Err(Error::MeasurementValueInvalid)));
1352    }
1353
1354    #[test]
1355    fn parse_where_nulls_accepted() {
1356        let input = "m,tag\x001=one,tag2=2 value=1 123
1357            m,tag1=o\0ne,tag2=2 value=1 123
1358            m,tag1=one,tag2=\0 value=1 123
1359            m,tag1=one,tag2=2 val\0ue=1 123
1360            m,tag1=one,tag2=2 value=\"v\0\" 123";
1361        let vals = parse(input);
1362        assert!(vals.is_ok());
1363        assert_eq!(vals.unwrap().len(), 5);
1364    }
1365
1366    #[test]
1367    fn parse_no_fields() {
1368        let input = "foo 1234";
1369        let vals = parse(input);
1370
1371        assert!(matches!(vals, Err(super::Error::FieldSetMissing)));
1372    }
1373
1374    #[test]
1375    fn parse_null_in_field_value() {
1376        let input = "m,tag1=one,tag2=2 value=\0 123";
1377        let vals = parse(input);
1378        assert!(vals.is_err());
1379    }
1380
1381    #[test]
1382    fn parse_single_field_integer() {
1383        let input = "foo asdf=23i 1234";
1384        let vals = parse(input).unwrap();
1385
1386        assert_eq!(vals[0].series.measurement, "foo");
1387        assert_eq!(vals[0].timestamp, Some(1234));
1388        assert_eq!(vals[0].field_set[0].0, "asdf");
1389        assert_eq!(vals[0].field_set[0].1.unwrap_i64(), 23);
1390    }
1391
1392    #[test]
1393    fn parse_single_field_unteger() {
1394        let input = "foo asdf=23u 1234";
1395        let vals = parse(input).unwrap();
1396
1397        assert_eq!(vals[0].series.measurement, "foo");
1398        assert_eq!(vals[0].timestamp, Some(1234));
1399        assert_eq!(vals[0].field_set[0].0, "asdf");
1400        assert_eq!(vals[0].field_set[0].1.unwrap_u64(), 23);
1401    }
1402
1403    #[test]
1404    fn parse_single_field_float_no_decimal() {
1405        let input = "foo asdf=44 546";
1406        let vals = parse(input).unwrap();
1407
1408        assert_eq!(vals[0].series.measurement, "foo");
1409        assert_eq!(vals[0].timestamp, Some(546));
1410        assert_eq!(vals[0].field_set[0].0, "asdf");
1411        assert!(approximately_equal(
1412            vals[0].field_set[0].1.unwrap_f64(),
1413            44.0
1414        ));
1415    }
1416
1417    #[test]
1418    fn parse_single_field_float_with_decimal() {
1419        let input = "foo asdf=3.74 123";
1420        let vals = parse(input).unwrap();
1421
1422        assert_eq!(vals[0].series.measurement, "foo");
1423        assert_eq!(vals[0].timestamp, Some(123));
1424        assert_eq!(vals[0].field_set[0].0, "asdf");
1425        assert!(approximately_equal(
1426            vals[0].field_set[0].1.unwrap_f64(),
1427            3.74
1428        ));
1429    }
1430
1431    #[test]
1432    fn parse_single_field_string() {
1433        let input = r#"foo asdf="the string value" 1234"#;
1434        let vals = parse(input).unwrap();
1435
1436        assert_eq!(vals[0].series.measurement, "foo");
1437        assert_eq!(vals[0].timestamp, Some(1234));
1438        assert_eq!(vals[0].field_set[0].0, "asdf");
1439        assert_eq!(&vals[0].field_set[0].1.unwrap_string(), "the string value");
1440    }
1441
1442    #[test]
1443    fn parse_single_field_bool() {
1444        let input = r#"foo asdf=true 1234"#;
1445        let vals = parse(input).unwrap();
1446
1447        assert_eq!(vals[0].series.measurement, "foo");
1448        assert_eq!(vals[0].timestamp, Some(1234));
1449        assert_eq!(vals[0].field_set[0].0, "asdf");
1450        assert!(vals[0].field_set[0].1.unwrap_bool());
1451    }
1452
1453    #[test]
1454    fn parse_string_values() {
1455        let test_data = vec![
1456            (r#"foo asdf="""#, ""),
1457            (r#"foo asdf="str val""#, "str val"),
1458            (r#"foo asdf="The \"string\" val""#, r#"The "string" val"#),
1459            (
1460                r#"foo asdf="The \"string w/ single double quote""#,
1461                r#"The "string w/ single double quote"#,
1462            ),
1463            // Examples from
1464            // https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_tutorial/#special-characters
1465            (r#"foo asdf="too hot/cold""#, r#"too hot/cold"#),
1466            (r#"foo asdf="too hot\cold""#, r"too hot\cold"),
1467            (r#"foo asdf="too hot\\cold""#, r"too hot\cold"),
1468            (r#"foo asdf="too hot\\\cold""#, r"too hot\\cold"),
1469            (r#"foo asdf="too hot\\\\cold""#, r"too hot\\cold"),
1470            (r#"foo asdf="too hot\\\\\cold""#, r"too hot\\\cold"),
1471        ];
1472
1473        for (input, expected_parsed_string_value) in test_data {
1474            let vals = parse(input).unwrap();
1475            assert_eq!(vals[0].series.tag_set, None);
1476            assert_eq!(vals[0].field_set.len(), 1);
1477            assert_eq!(vals[0].field_set[0].0, "asdf");
1478            assert_eq!(
1479                &vals[0].field_set[0].1.unwrap_string(),
1480                expected_parsed_string_value
1481            );
1482        }
1483    }
1484
1485    #[test]
1486    fn parse_bool_values() {
1487        let test_data = vec![
1488            (r#"foo asdf=t"#, true),
1489            (r#"foo asdf=T"#, true),
1490            (r#"foo asdf=true"#, true),
1491            (r#"foo asdf=True"#, true),
1492            (r#"foo asdf=TRUE"#, true),
1493            (r#"foo asdf=f"#, false),
1494            (r#"foo asdf=F"#, false),
1495            (r#"foo asdf=false"#, false),
1496            (r#"foo asdf=False"#, false),
1497            (r#"foo asdf=FALSE"#, false),
1498        ];
1499
1500        for (input, expected_parsed_bool_value) in test_data {
1501            let vals = parse(input).unwrap();
1502            assert_eq!(vals[0].series.tag_set, None);
1503            assert_eq!(vals[0].field_set.len(), 1);
1504            assert_eq!(vals[0].field_set[0].0, "asdf");
1505            assert_eq!(
1506                vals[0].field_set[0].1.unwrap_bool(),
1507                expected_parsed_bool_value
1508            );
1509        }
1510    }
1511
1512    #[test]
1513    fn parse_two_fields_integer() {
1514        let input = "foo asdf=23i,bar=5i 1234";
1515        let vals = parse(input).unwrap();
1516
1517        assert_eq!(vals[0].series.measurement, "foo");
1518        assert_eq!(vals[0].timestamp, Some(1234));
1519
1520        assert_eq!(vals[0].field_set[0].0, "asdf");
1521        assert_eq!(vals[0].field_set[0].1.unwrap_i64(), 23);
1522
1523        assert_eq!(vals[0].field_set[1].0, "bar");
1524        assert_eq!(vals[0].field_set[1].1.unwrap_i64(), 5);
1525    }
1526
1527    #[test]
1528    fn parse_two_fields_unteger() {
1529        let input = "foo asdf=23u,bar=5u 1234";
1530        let vals = parse(input).unwrap();
1531
1532        assert_eq!(vals[0].series.measurement, "foo");
1533        assert_eq!(vals[0].timestamp, Some(1234));
1534
1535        assert_eq!(vals[0].field_set[0].0, "asdf");
1536        assert_eq!(vals[0].field_set[0].1.unwrap_u64(), 23);
1537
1538        assert_eq!(vals[0].field_set[1].0, "bar");
1539        assert_eq!(vals[0].field_set[1].1.unwrap_u64(), 5);
1540    }
1541
1542    #[test]
1543    fn parse_two_fields_float() {
1544        let input = "foo asdf=23.1,bar=5 1234";
1545        let vals = parse(input).unwrap();
1546
1547        assert_eq!(vals[0].series.measurement, "foo");
1548        assert_eq!(vals[0].timestamp, Some(1234));
1549
1550        assert_eq!(vals[0].field_set[0].0, "asdf");
1551        assert!(approximately_equal(
1552            vals[0].field_set[0].1.unwrap_f64(),
1553            23.1
1554        ));
1555
1556        assert_eq!(vals[0].field_set[1].0, "bar");
1557        assert!(approximately_equal(
1558            vals[0].field_set[1].1.unwrap_f64(),
1559            5.0
1560        ));
1561    }
1562
1563    #[test]
1564    fn parse_mixed_field_types() {
1565        let input = r#"foo asdf=23.1,bar=-5i,qux=9u,baz="the string",frab=false 1234"#;
1566        let vals = parse(input).unwrap();
1567
1568        assert_eq!(vals[0].series.measurement, "foo");
1569        assert_eq!(vals[0].timestamp, Some(1234));
1570
1571        assert_eq!(vals[0].field_set[0].0, "asdf");
1572        assert!(approximately_equal(
1573            vals[0].field_set[0].1.unwrap_f64(),
1574            23.1
1575        ));
1576
1577        assert_eq!(vals[0].field_set[1].0, "bar");
1578        assert_eq!(vals[0].field_set[1].1.unwrap_i64(), -5);
1579
1580        assert_eq!(vals[0].field_set[2].0, "qux");
1581        assert_eq!(vals[0].field_set[2].1.unwrap_u64(), 9);
1582
1583        assert_eq!(vals[0].field_set[3].0, "baz");
1584        assert_eq!(vals[0].field_set[3].1.unwrap_string(), "the string");
1585
1586        assert_eq!(vals[0].field_set[4].0, "frab");
1587        assert!(!vals[0].field_set[4].1.unwrap_bool());
1588    }
1589
1590    #[test]
1591    fn parse_negative_integer() {
1592        let input = "m0 field=-1i 99";
1593        let vals = parse(input).unwrap();
1594
1595        assert_eq!(vals.len(), 1);
1596        assert_eq!(vals[0].field_set[0].1.unwrap_i64(), -1);
1597    }
1598
1599    #[test]
1600    fn parse_negative_uinteger() {
1601        let input = "m0 field=-1u 99";
1602        let parsed = parse(input);
1603
1604        assert!(
1605            matches!(parsed, Err(super::Error::CannotParseEntireLine { .. })),
1606            "Wrong error: {parsed:?}",
1607        );
1608    }
1609
1610    #[test]
1611    fn parse_scientific_float() {
1612        // Positive tests
1613        let input = "m0 field=-1.234456e+06 1615869152385000000";
1614        let vals = parse(input).unwrap();
1615        assert_eq!(vals.len(), 1);
1616
1617        let input = "m0 field=-1.234456E+3 1615869152385000000";
1618        let vals = parse(input).unwrap();
1619        assert_eq!(vals.len(), 1);
1620
1621        let input = "m0 field=1.234456e+02 1615869152385000000";
1622        let vals = parse(input).unwrap();
1623        assert_eq!(vals.len(), 1);
1624
1625        let input = "m0 field=1.234456E+16 1615869152385000000";
1626        let vals = parse(input).unwrap();
1627        assert_eq!(vals.len(), 1);
1628
1629        let input = "m0 field=1.234456E-16";
1630        let vals = parse(input).unwrap();
1631        assert_eq!(vals.len(), 1);
1632
1633        let input = "m0 field=1.234456e-03";
1634        let vals = parse(input).unwrap();
1635        assert_eq!(vals.len(), 1);
1636
1637        let input = "m0 field=1.234456e-0";
1638        let vals = parse(input).unwrap();
1639        assert_eq!(vals.len(), 1);
1640
1641        let input = "m0 field=1e-0";
1642        let vals = parse(input).unwrap();
1643        assert_eq!(vals.len(), 1);
1644        assert_eq!(vals[0].field_value("field"), Some(&FieldValue::F64(1.0)));
1645
1646        let input = "m0 field=-1e-0";
1647        let vals = parse(input).unwrap();
1648        assert_eq!(vals.len(), 1);
1649        assert_eq!(vals[0].field_value("field"), Some(&FieldValue::F64(-1.0)));
1650
1651        // NO "+" sign is accepted by IDPE
1652        let input = "m0 field=-1.234456e06 1615869152385000000";
1653        let vals = parse(input).unwrap();
1654        assert_eq!(vals.len(), 1);
1655        assert_float_field(&vals[0], "field", -1.234456e06);
1656
1657        let input = "m0 field=1.234456e06 1615869152385000000";
1658        let vals = parse(input).unwrap();
1659        assert_eq!(vals.len(), 1);
1660        assert_float_field(&vals[0], "field", 1.234456e06);
1661
1662        let input = "m0 field=-1.234456E06 1615869152385000000";
1663        let vals = parse(input).unwrap();
1664        assert_eq!(vals.len(), 1);
1665        assert_float_field(&vals[0], "field", -1.234456e06);
1666
1667        let input = "m0 field=1.234456E06 1615869152385000000";
1668        let vals = parse(input).unwrap();
1669        assert_eq!(vals.len(), 1);
1670        assert_float_field(&vals[0], "field", 1.234456e06);
1671
1672        /////////////////////
1673        // Negative tests
1674
1675        // No digits after e
1676        let input = "m0 field=-1.234456e 1615869152385000000";
1677        let parsed = parse(input);
1678        assert!(
1679            matches!(parsed, Err(super::Error::CannotParseEntireLine { .. })),
1680            "Wrong error: {parsed:?}",
1681        );
1682
1683        let input = "m0 field=-1.234456e+ 1615869152385000000";
1684        let parsed = parse(input);
1685        assert!(
1686            matches!(parsed, Err(super::Error::CannotParseEntireLine { .. })),
1687            "Wrong error: {parsed:?}",
1688        );
1689
1690        let input = "m0 field=-1.234456E 1615869152385000000";
1691        let parsed = parse(input);
1692        assert!(
1693            matches!(parsed, Err(super::Error::CannotParseEntireLine { .. })),
1694            "Wrong error: {parsed:?}",
1695        );
1696
1697        let input = "m0 field=-1.234456E+ 1615869152385000000";
1698        let parsed = parse(input);
1699        assert!(
1700            matches!(parsed, Err(super::Error::CannotParseEntireLine { .. })),
1701            "Wrong error: {parsed:?}",
1702        );
1703
1704        let input = "m0 field=-1.234456E-";
1705        let parsed = parse(input);
1706        assert!(
1707            matches!(parsed, Err(super::Error::CannotParseEntireLine { .. })),
1708            "Wrong error: {parsed:?}",
1709        );
1710    }
1711
1712    #[test]
1713    fn parse_negative_float() {
1714        let input = "m0 field2=-1 99";
1715        let vals = parse(input).unwrap();
1716
1717        assert_eq!(vals.len(), 1);
1718        assert!(approximately_equal(
1719            vals[0].field_set[0].1.unwrap_f64(),
1720            -1.0
1721        ));
1722    }
1723
1724    #[test]
1725    fn parse_out_of_range_integer() {
1726        let input = "m0 field=99999999999999999999999999999999i 99";
1727        let parsed = parse(input);
1728
1729        assert!(
1730            matches!(parsed, Err(super::Error::IntegerValueInvalid { .. })),
1731            "Wrong error: {parsed:?}",
1732        );
1733    }
1734
1735    #[test]
1736    fn parse_out_of_range_uinteger() {
1737        let input = "m0 field=99999999999999999999999999999999u 99";
1738        let parsed = parse(input);
1739
1740        assert!(
1741            matches!(parsed, Err(super::Error::UIntegerValueInvalid { .. })),
1742            "Wrong error: {parsed:?}",
1743        );
1744    }
1745
1746    #[test]
1747    fn parse_out_of_range_float() {
1748        // this works since rust 1.55
1749        let input = format!("m0 field={val}.{val} 99", val = "9".repeat(200));
1750        let vals = parse(&input).unwrap();
1751
1752        assert_eq!(vals.len(), 1);
1753        assert!(approximately_equal(
1754            vals[0].field_set[0].1.unwrap_f64(),
1755            1e200f64
1756        ));
1757
1758        // even very long inputs now work
1759        let input = format!("m0 field={val}.{val} 99", val = "9".repeat(1_000));
1760        let vals = parse(&input).unwrap();
1761
1762        assert_eq!(vals.len(), 1);
1763        assert!(vals[0].field_set[0].1.unwrap_f64().is_infinite());
1764    }
1765
1766    #[test]
1767    fn parse_tag_set_included_in_series() {
1768        let input = "foo,tag1=1,tag2=2 value=1 123";
1769        let vals = parse(input).unwrap();
1770
1771        assert_eq!(vals[0].series.measurement, "foo");
1772
1773        assert_eq!(vals[0].series.tag_set.as_ref().unwrap()[0].0, "tag1");
1774        assert_eq!(vals[0].series.tag_set.as_ref().unwrap()[0].1, "1");
1775
1776        assert_eq!(vals[0].series.tag_set.as_ref().unwrap()[1].0, "tag2");
1777        assert_eq!(vals[0].series.tag_set.as_ref().unwrap()[1].1, "2");
1778
1779        assert_eq!(vals[0].field_set[0].0, "value");
1780    }
1781
1782    #[test]
1783    fn parse_tag_set_unsorted() {
1784        let input = "foo,tag2=2,tag1=1";
1785        let (remaining, series) = series(input).unwrap();
1786
1787        assert!(remaining.is_empty());
1788        assert_eq!(series.generate_base().unwrap(), "foo,tag1=1,tag2=2");
1789    }
1790
1791    #[test]
1792    fn parse_tag_set_duplicate_tags() {
1793        let input = "foo,tag=1,tag=2";
1794        let (remaining, series) = series(input).unwrap();
1795
1796        assert!(remaining.is_empty());
1797        let err = series
1798            .generate_base()
1799            .expect_err("Parsing duplicate tags should fail");
1800
1801        assert_eq!(
1802            err.to_string(),
1803            r#"Must not contain duplicate tags, but "tag" was repeated"#
1804        );
1805    }
1806
1807    #[test]
1808    fn parse_multiple_lines_become_multiple_points() {
1809        let input = r#"foo value1=1i 123
1810foo value2=2i 123"#;
1811        let vals = parse(input).unwrap();
1812
1813        assert_eq!(vals[0].series.measurement, "foo");
1814        assert_eq!(vals[0].timestamp, Some(123));
1815        assert_eq!(vals[0].field_set[0].0, "value1");
1816        assert_eq!(vals[0].field_set[0].1.unwrap_i64(), 1);
1817
1818        assert_eq!(vals[1].series.measurement, "foo");
1819        assert_eq!(vals[1].timestamp, Some(123));
1820        assert_eq!(vals[1].field_set[0].0, "value2");
1821        assert_eq!(vals[1].field_set[0].1.unwrap_i64(), 2);
1822    }
1823
1824    #[test]
1825    fn parse_multiple_measurements_become_multiple_points() {
1826        let input = r#"foo value1=1i 123
1827bar value2=2i 123"#;
1828        let vals = parse(input).unwrap();
1829
1830        assert_eq!(vals[0].series.measurement, "foo");
1831        assert_eq!(vals[0].timestamp, Some(123));
1832        assert_eq!(vals[0].field_set[0].0, "value1");
1833        assert_eq!(vals[0].field_set[0].1.unwrap_i64(), 1);
1834
1835        assert_eq!(vals[1].series.measurement, "bar");
1836        assert_eq!(vals[1].timestamp, Some(123));
1837        assert_eq!(vals[1].field_set[0].0, "value2");
1838        assert_eq!(vals[1].field_set[0].1.unwrap_i64(), 2);
1839    }
1840
1841    #[test]
1842    fn parse_trailing_whitespace_is_fine() {
1843        let input = r#"foo,tag=val value1=1i 123
1844
1845"#;
1846        let vals = parse(input).unwrap();
1847        assert_eq!(vals.len(), 1);
1848
1849        assert_eq!(vals[0].series.measurement, "foo");
1850        assert_eq!(vals[0].timestamp, Some(123));
1851        assert_eq!(vals[0].field_set[0].0, "value1");
1852        assert_eq!(vals[0].field_set[0].1.unwrap_i64(), 1);
1853    }
1854
1855    #[test]
1856    fn parse_negative_timestamp() {
1857        let input = r#"foo value1=1i -123"#;
1858        let vals = parse(input).unwrap();
1859
1860        assert_eq!(vals[0].series.measurement, "foo");
1861        assert_eq!(vals[0].timestamp, Some(-123));
1862        assert_eq!(vals[0].field_set[0].0, "value1");
1863        assert_eq!(vals[0].field_set[0].1.unwrap_i64(), 1);
1864    }
1865
1866    #[test]
1867    fn parse_out_of_range_timestamp() {
1868        let input = "m0 field=1i 99999999999999999999999999999999";
1869        let parsed = parse(input);
1870
1871        assert!(
1872            matches!(parsed, Err(super::Error::TimestampValueInvalid { .. })),
1873            "Wrong error: {parsed:?}",
1874        );
1875    }
1876
1877    #[test]
1878    fn parse_blank_lines_are_ignored() {
1879        let input = "\n\n\n";
1880        let vals = parse(input).unwrap();
1881
1882        assert!(vals.is_empty());
1883    }
1884
1885    #[test]
1886    fn parse_commented_lines_are_ignored() {
1887        let input = "# comment";
1888        let vals = parse(input).unwrap();
1889
1890        assert!(vals.is_empty());
1891    }
1892
1893    #[test]
1894    fn parse_multiple_whitespace_between_elements_is_allowed() {
1895        let input = "  measurement  a=1i  123  ";
1896        let vals = parse(input).unwrap();
1897
1898        assert_eq!(vals[0].series.measurement, "measurement");
1899        assert_eq!(vals[0].timestamp, Some(123));
1900        assert_eq!(vals[0].field_set[0].0, "a");
1901        assert_eq!(vals[0].field_set[0].1.unwrap_i64(), 1);
1902    }
1903
1904    macro_rules! assert_fully_parsed {
1905        ($parse_result:expr, $output:expr $(,)?) => {{
1906            let (remaining, parsed) = $parse_result.unwrap();
1907
1908            assert!(
1909                remaining.is_empty(),
1910                "Some input remained to be parsed: {:?}",
1911                remaining,
1912            );
1913            assert_eq!(parsed, $output, "Did not parse the expected output");
1914        }};
1915    }
1916
1917    #[test]
1918    fn measurement_allows_escaping_comma() {
1919        assert_fully_parsed!(measurement(r"wea\,ther"), r#"wea,ther"#);
1920    }
1921
1922    #[test]
1923    fn measurement_allows_escaping_space() {
1924        assert_fully_parsed!(measurement(r"wea\ ther"), r#"wea ther"#);
1925    }
1926
1927    #[test]
1928    fn measurement_allows_escaping_backslash() {
1929        assert_fully_parsed!(measurement(r"\\wea\\ther"), r"\wea\ther");
1930    }
1931
1932    #[test]
1933    fn measurement_allows_backslash_with_unknown_escape() {
1934        assert_fully_parsed!(measurement(r"\wea\ther"), r"\wea\ther");
1935    }
1936
1937    #[test]
1938    fn measurement_allows_literal_newline_as_unknown_escape() {
1939        assert_fully_parsed!(
1940            measurement(
1941                r"weat\
1942her"
1943            ),
1944            "weat\\\nher",
1945        );
1946    }
1947
1948    #[test]
1949    fn measurement_disallows_literal_newline() {
1950        let (remaining, parsed) = measurement(
1951            r#"weat
1952her"#,
1953        )
1954        .unwrap();
1955        assert_eq!(parsed, "weat");
1956        assert_eq!(remaining, "\nher");
1957    }
1958
1959    #[test]
1960    fn measurement_disallows_ending_in_backslash() {
1961        let parsed = measurement(r"weather\");
1962        assert!(matches!(
1963            parsed,
1964            Err(nom::Err::Failure(super::Error::EndsWithBackslash))
1965        ));
1966    }
1967
1968    #[test]
1969    fn tag_key_allows_escaping_comma() {
1970        assert_fully_parsed!(tag_key(r"wea\,ther"), r#"wea,ther"#);
1971    }
1972
1973    #[test]
1974    fn tag_key_allows_escaping_equal() {
1975        assert_fully_parsed!(tag_key(r"wea\=ther"), r#"wea=ther"#);
1976    }
1977
1978    #[test]
1979    fn tag_key_allows_escaping_space() {
1980        assert_fully_parsed!(tag_key(r"wea\ ther"), r#"wea ther"#);
1981    }
1982
1983    #[test]
1984    fn tag_key_allows_escaping_backslash() {
1985        assert_fully_parsed!(tag_key(r"\\wea\\ther"), r"\wea\ther");
1986    }
1987
1988    #[test]
1989    fn tag_key_allows_backslash_with_unknown_escape() {
1990        assert_fully_parsed!(tag_key(r"\wea\ther"), r"\wea\ther");
1991    }
1992
1993    #[test]
1994    fn tag_key_allows_literal_newline_as_unknown_escape() {
1995        assert_fully_parsed!(
1996            tag_key(
1997                r"weat\
1998her"
1999            ),
2000            "weat\\\nher",
2001        );
2002    }
2003
2004    #[test]
2005    fn tag_key_disallows_literal_newline() {
2006        let (remaining, parsed) = tag_key(
2007            r#"weat
2008her"#,
2009        )
2010        .unwrap();
2011        assert_eq!(parsed, "weat");
2012        assert_eq!(remaining, "\nher");
2013    }
2014
2015    #[test]
2016    fn tag_key_disallows_ending_in_backslash() {
2017        let parsed = tag_key(r"weather\");
2018        assert!(matches!(
2019            parsed,
2020            Err(nom::Err::Failure(super::Error::EndsWithBackslash))
2021        ));
2022    }
2023
2024    #[test]
2025    fn tag_value_allows_escaping_comma() {
2026        assert_fully_parsed!(tag_value(r"wea\,ther"), r#"wea,ther"#);
2027    }
2028
2029    #[test]
2030    fn tag_value_allows_escaping_equal() {
2031        assert_fully_parsed!(tag_value(r"wea\=ther"), r#"wea=ther"#);
2032    }
2033
2034    #[test]
2035    fn tag_value_allows_escaping_space() {
2036        assert_fully_parsed!(tag_value(r"wea\ ther"), r#"wea ther"#);
2037    }
2038
2039    #[test]
2040    fn tag_value_allows_escaping_backslash() {
2041        assert_fully_parsed!(tag_value(r"\\wea\\ther"), r"\wea\ther");
2042    }
2043
2044    #[test]
2045    fn tag_value_allows_backslash_with_unknown_escape() {
2046        assert_fully_parsed!(tag_value(r"\wea\ther"), r"\wea\ther");
2047    }
2048
2049    #[test]
2050    fn tag_value_allows_literal_newline_as_unknown_escape() {
2051        assert_fully_parsed!(
2052            tag_value(
2053                r"weat\
2054her"
2055            ),
2056            "weat\\\nher",
2057        );
2058    }
2059
2060    #[test]
2061    fn tag_value_disallows_literal_newline() {
2062        let (remaining, parsed) = tag_value(
2063            r#"weat
2064her"#,
2065        )
2066        .unwrap();
2067        assert_eq!(parsed, "weat");
2068        assert_eq!(remaining, "\nher");
2069    }
2070
2071    #[test]
2072    fn tag_value_disallows_ending_in_backslash() {
2073        let parsed = tag_value(r"weather\");
2074        assert!(matches!(
2075            parsed,
2076            Err(nom::Err::Failure(super::Error::EndsWithBackslash))
2077        ));
2078    }
2079
2080    #[test]
2081    fn field_key_allows_escaping_comma() {
2082        assert_fully_parsed!(field_key(r"wea\,ther"), r#"wea,ther"#);
2083    }
2084
2085    #[test]
2086    fn field_key_allows_escaping_equal() {
2087        assert_fully_parsed!(field_key(r"wea\=ther"), r#"wea=ther"#);
2088    }
2089
2090    #[test]
2091    fn field_key_allows_escaping_space() {
2092        assert_fully_parsed!(field_key(r"wea\ ther"), r#"wea ther"#);
2093    }
2094
2095    #[test]
2096    fn field_key_allows_escaping_backslash() {
2097        assert_fully_parsed!(field_key(r"\\wea\\ther"), r"\wea\ther");
2098    }
2099
2100    #[test]
2101    fn field_key_allows_backslash_with_unknown_escape() {
2102        assert_fully_parsed!(field_key(r"\wea\ther"), r"\wea\ther");
2103    }
2104
2105    #[test]
2106    fn field_key_allows_literal_newline_as_unknown_escape() {
2107        assert_fully_parsed!(
2108            field_key(
2109                r"weat\
2110her"
2111            ),
2112            "weat\\\nher",
2113        );
2114    }
2115
2116    #[test]
2117    fn field_key_disallows_literal_newline() {
2118        let (remaining, parsed) = field_key(
2119            r#"weat
2120her"#,
2121        )
2122        .unwrap();
2123        assert_eq!(parsed, "weat");
2124        assert_eq!(remaining, "\nher");
2125    }
2126
2127    #[test]
2128    fn field_key_disallows_ending_in_backslash() {
2129        let parsed = field_key(r"weather\");
2130        assert!(matches!(
2131            parsed,
2132            Err(nom::Err::Failure(super::Error::EndsWithBackslash))
2133        ));
2134    }
2135
2136    #[test]
2137    fn parse_no_time() {
2138        let input = "foo,tag0=value1 asdf=23.1,bar=5i";
2139        let vals = parse(input).unwrap();
2140
2141        assert_eq!(vals[0].series.measurement, "foo");
2142        assert_eq!(vals[0].series.tag_set.as_ref().unwrap()[0].0, "tag0");
2143        assert_eq!(vals[0].series.tag_set.as_ref().unwrap()[0].1, "value1");
2144
2145        assert_eq!(vals[0].timestamp, None);
2146
2147        assert_eq!(vals[0].field_set[0].0, "asdf");
2148        assert!(approximately_equal(
2149            vals[0].field_set[0].1.unwrap_f64(),
2150            23.1
2151        ));
2152
2153        assert_eq!(vals[0].field_set[1].0, "bar");
2154        assert_eq!(vals[0].field_set[1].1.unwrap_i64(), 5);
2155    }
2156
2157    #[test]
2158    fn parse_advance_after_error() {
2159        // Note that the first line has an error (23.1.22 is not a number),
2160        // but there is valid data afterwrds,
2161        let input = "foo,tag0=value1 asdf=23.1.22,jkl=4\n\
2162                     foo,tag0=value2 asdf=22.1,jkl=5";
2163
2164        let vals: Vec<_> = super::parse_lines(input).collect();
2165
2166        assert_eq!(vals.len(), 2);
2167        assert!(vals[0].is_err());
2168        assert_eq!(
2169            format!("{:?}", &vals[0]),
2170            "Err(CannotParseEntireLine { trailing_content: \".22,jkl=4\" })"
2171        );
2172
2173        assert!(vals[1].is_ok());
2174        let parsed_line = vals[1].as_ref().expect("second line succeeded");
2175        assert_eq!(parsed_line.series.measurement, "foo");
2176        assert_eq!(parsed_line.series.tag_set.as_ref().unwrap()[0].0, "tag0");
2177        assert_eq!(parsed_line.series.tag_set.as_ref().unwrap()[0].1, "value2");
2178
2179        assert_eq!(parsed_line.timestamp, None);
2180
2181        assert_eq!(parsed_line.field_set[0].0, "asdf");
2182        assert!(approximately_equal(
2183            parsed_line.field_set[0].1.unwrap_f64(),
2184            22.1
2185        ));
2186
2187        assert_eq!(parsed_line.field_set[1].0, "jkl");
2188        assert!(approximately_equal(
2189            parsed_line.field_set[1].1.unwrap_f64(),
2190            5.
2191        ));
2192    }
2193
2194    #[test]
2195    fn field_value_display() {
2196        assert_eq!(FieldValue::I64(-42).to_string(), "-42i");
2197        assert_eq!(FieldValue::U64(42).to_string(), "42u");
2198        assert_eq!(FieldValue::F64(42.11).to_string(), "42.11");
2199        assert_eq!(
2200            FieldValue::String(EscapedStr::from("foo")).to_string(),
2201            "foo"
2202        );
2203        assert_eq!(FieldValue::Boolean(true).to_string(), "true");
2204        assert_eq!(FieldValue::Boolean(false).to_string(), "false");
2205    }
2206
2207    #[test]
2208    fn series_display_no_tags() {
2209        let series = Series {
2210            raw_input: "foo",
2211            measurement: EscapedStr::from("m"),
2212            tag_set: None,
2213        };
2214        assert_eq!(series.to_string(), "m");
2215    }
2216
2217    #[test]
2218    fn series_display_one_tag() {
2219        let series = Series {
2220            raw_input: "foo",
2221            measurement: EscapedStr::from("m"),
2222            tag_set: Some(smallvec![(
2223                EscapedStr::from("tag1"),
2224                EscapedStr::from("val1")
2225            )]),
2226        };
2227        assert_eq!(series.to_string(), "m,tag1=val1");
2228    }
2229
2230    #[test]
2231    fn series_display_two_tags() {
2232        let series = Series {
2233            raw_input: "foo",
2234            measurement: EscapedStr::from("m"),
2235            tag_set: Some(smallvec![
2236                (EscapedStr::from("tag1"), EscapedStr::from("val1")),
2237                (EscapedStr::from("tag2"), EscapedStr::from("val2")),
2238            ]),
2239        };
2240        assert_eq!(series.to_string(), "m,tag1=val1,tag2=val2");
2241    }
2242
2243    #[test]
2244    fn parsed_line_display_one_field_no_timestamp() {
2245        let series = Series {
2246            raw_input: "foo",
2247            measurement: EscapedStr::from("m"),
2248            tag_set: Some(smallvec![(
2249                EscapedStr::from("tag1"),
2250                EscapedStr::from("val1")
2251            ),]),
2252        };
2253        let field_set = smallvec![(EscapedStr::from("field1"), FieldValue::F64(42.1))];
2254
2255        let parsed_line = ParsedLine {
2256            series,
2257            field_set,
2258            timestamp: None,
2259        };
2260
2261        assert_eq!(parsed_line.to_string(), "m,tag1=val1 field1=42.1");
2262    }
2263
2264    #[test]
2265    fn parsed_line_display_one_field_timestamp() {
2266        let series = Series {
2267            raw_input: "foo",
2268            measurement: EscapedStr::from("m"),
2269            tag_set: Some(smallvec![(
2270                EscapedStr::from("tag1"),
2271                EscapedStr::from("val1")
2272            ),]),
2273        };
2274        let field_set = smallvec![(EscapedStr::from("field1"), FieldValue::F64(42.1))];
2275
2276        let parsed_line = ParsedLine {
2277            series,
2278            field_set,
2279            timestamp: Some(33),
2280        };
2281
2282        assert_eq!(parsed_line.to_string(), "m,tag1=val1 field1=42.1 33");
2283    }
2284
2285    #[test]
2286    fn parsed_line_display_two_fields_timestamp() {
2287        let series = Series {
2288            raw_input: "foo",
2289            measurement: EscapedStr::from("m"),
2290            tag_set: Some(smallvec![(
2291                EscapedStr::from("tag1"),
2292                EscapedStr::from("val1")
2293            ),]),
2294        };
2295        let field_set = smallvec![
2296            (EscapedStr::from("field1"), FieldValue::F64(42.1)),
2297            (EscapedStr::from("field2"), FieldValue::Boolean(false)),
2298        ];
2299
2300        let parsed_line = ParsedLine {
2301            series,
2302            field_set,
2303            timestamp: Some(33),
2304        };
2305
2306        assert_eq!(
2307            parsed_line.to_string(),
2308            "m,tag1=val1 field1=42.1,field2=false 33"
2309        );
2310    }
2311
2312    #[test]
2313    fn parsed_line_display_escaped() {
2314        let series = Series {
2315            raw_input: "foo",
2316            measurement: EscapedStr::from("m,and m"),
2317            tag_set: Some(smallvec![(
2318                EscapedStr::from("tag ,1"),
2319                EscapedStr::from("val ,1")
2320            ),]),
2321        };
2322        let field_set = smallvec![(
2323            EscapedStr::from("field ,1"),
2324            FieldValue::String(EscapedStr::from("Foo\"Bar"))
2325        ),];
2326
2327        let parsed_line = ParsedLine {
2328            series,
2329            field_set,
2330            timestamp: Some(33),
2331        };
2332
2333        assert_eq!(
2334            parsed_line.to_string(),
2335            r#"m\,and\ m,tag\ \,1=val\ \,1 field\ \,1=Foo\"Bar 33"#
2336        );
2337    }
2338
2339    #[test]
2340    fn field_value_returned() {
2341        let input = r#"foo asdf=true 1234"#;
2342        let vals = parse(input).unwrap();
2343
2344        assert!(vals[0].field_value("asdf").unwrap().unwrap_bool());
2345    }
2346
2347    #[test]
2348    fn field_value_missing() {
2349        let input = r#"foo asdf=true 1234"#;
2350        let vals = parse(input).unwrap();
2351
2352        assert_eq!(vals[0].field_value("jkl"), None);
2353    }
2354
2355    #[test]
2356    fn tag_value_returned() {
2357        let input = r#"foo,test=stuff asdf=true 1234"#;
2358        let vals = parse(input).unwrap();
2359
2360        assert_eq!(*vals[0].tag_value("test").unwrap(), "stuff");
2361    }
2362
2363    #[test]
2364    fn tag_value_missing() {
2365        let input = r#"foo,test=stuff asdf=true 1234"#;
2366        let vals = parse(input).unwrap();
2367
2368        assert_eq!(vals[0].tag_value("asdf"), None);
2369    }
2370
2371    #[test]
2372    fn test_field_value_same_type() {
2373        // True cases
2374        assert!(FieldValue::I64(0).is_same_type(&FieldValue::I64(42)));
2375        assert!(FieldValue::U64(0).is_same_type(&FieldValue::U64(42)));
2376        assert!(FieldValue::F64(0.0).is_same_type(&FieldValue::F64(4.2)));
2377        // String & String
2378        assert!(
2379            FieldValue::String(EscapedStr::CopiedValue("bananas".to_string())).is_same_type(
2380                &FieldValue::String(EscapedStr::CopiedValue("platanos".to_string()))
2381            )
2382        );
2383        // str & str
2384        assert!(FieldValue::String(EscapedStr::SingleSlice("bananas"))
2385            .is_same_type(&FieldValue::String(EscapedStr::SingleSlice("platanos"))));
2386        // str & String
2387        assert!(
2388            FieldValue::String(EscapedStr::SingleSlice("bananas")).is_same_type(
2389                &FieldValue::String(EscapedStr::CopiedValue("platanos".to_string()))
2390            )
2391        );
2392
2393        assert!(FieldValue::Boolean(true).is_same_type(&FieldValue::Boolean(false)));
2394
2395        // Some false cases
2396        assert!(!FieldValue::I64(0).is_same_type(&FieldValue::U64(42)));
2397        assert!(!FieldValue::U64(0).is_same_type(&FieldValue::I64(42)));
2398        assert!(!FieldValue::F64(0.0).is_same_type(&FieldValue::U64(42)));
2399        assert!(
2400            !FieldValue::String(EscapedStr::CopiedValue("bananas".to_string()))
2401                .is_same_type(&FieldValue::U64(42))
2402        );
2403        assert!(!FieldValue::Boolean(true).is_same_type(&FieldValue::U64(42)));
2404    }
2405
2406    /// Assert that the field named `field_name` has a float value
2407    /// within 0.0001% of `expected_value`, panic'ing if not
2408    fn assert_float_field(parsed_line: &ParsedLine<'_>, field_name: &str, expected_value: f64) {
2409        let field_value = parsed_line
2410            .field_value(field_name)
2411            .expect("did not contain field name");
2412
2413        let actual_value = if let FieldValue::F64(v) = field_value {
2414            *v
2415        } else {
2416            panic!("field {field_name} had value {field_value:?}, expected F64");
2417        };
2418
2419        assert!(approximately_equal(expected_value, actual_value));
2420    }
2421}