1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use super::grammar;

/// Default fields that represent the search path when a Datadog tag/facet is not provided.
static DEFAULT_FIELDS: &[&str] = &[
    "message",
    "custom.error.message",
    "custom.error.stack",
    "custom.title",
    "_default_",
];

/// Attributes that represent special fields in Datadog.
static RESERVED_ATTRIBUTES: &[&str] = &[
    "host",
    "source",
    "status",
    "service",
    "trace_id",
    "message",
    "timestamp",
    "tags",
];

/// Describes a field to search on.
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub enum Field {
    /// Default field (when tag/facet isn't provided)
    Default(String),

    /// Reserved field that receives special treatment in Datadog.
    Reserved(String),

    /// A facet -- i.e. started with `@`, transformed to `custom.*`
    Facet(String),

    /// Tag type - i.e. search in the `tags` field.
    Tag(String),
}

impl Field {
    pub fn as_str(&self) -> &str {
        match self {
            Self::Default(ref s) => s,
            Self::Reserved(ref s) => s,
            Self::Facet(ref s) => s,
            Self::Tag(ref s) => s,
        }
    }
}

/// Converts a field/facet name to the VRL equivalent. Datadog payloads have a `message` field
/// (which is used whenever the default field is encountered. Facets are hosted on .custom.*.
pub fn normalize_fields<T: AsRef<str>>(value: T) -> Vec<Field> {
    let value = value.as_ref();
    if value.eq(grammar::DEFAULT_FIELD) {
        return DEFAULT_FIELDS
            .iter()
            .map(|s| Field::Default((*s).to_owned()))
            .collect();
    }

    let field = match value.replace('@', "custom.") {
        v if value.starts_with('@') => Field::Facet(v),
        v if DEFAULT_FIELDS.contains(&v.as_ref()) => Field::Default(v),
        v if RESERVED_ATTRIBUTES.contains(&v.as_ref()) => Field::Reserved(v),
        v => Field::Tag(v),
    };

    vec![field]
}