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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use std::borrow::Cow;
use std::fmt;

use serde::Serialize;

/// Identifies which filter dropped an event for which reason.
///
/// Ported from Sentry's same-named "enum". The enum variants are fed into outcomes in kebap-case
/// (e.g.  "browser-extensions")
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Hash)]
pub enum FilterStatKey {
    /// Filtered by ip address.
    IpAddress,

    /// Filtered by release name (version).
    ReleaseVersion,

    /// Filtered by error message.
    ErrorMessage,

    /// Filtered by browser extension.
    BrowserExtensions,

    /// Filtered by legacy browser version.
    LegacyBrowsers,

    /// Filtered due to localhost restriction.
    Localhost,

    /// Filtered as known web crawler.
    WebCrawlers,

    /// Filtered due to invalid CSP policy.
    InvalidCsp,

    /// Filtered due to the fact that it was a call to a filtered transaction
    FilteredTransactions,

    /// Filtered due to name being denied.
    DeniedName,

    /// Filtered due to the namespace being disabled.
    DisabledNamespace,

    /// Filtered due to a generic filter.
    GenericFilter(String),
}

// An event grouped to a removed group.
//
// Not returned by any filters implemented in Rust.
// DiscardedHash,

// Invalid CORS header.
//
// NOTE: Although cors is in the Sentry's FilterStatKey enum it is used for
// Invalid outcomes and therefore should logically belong to OutcomeInvalidReason
// that is why it was commented here and moved to OutcomeInvalidReason enum
// Cors,

impl FilterStatKey {
    /// Returns the string identifier of the filter stat key.
    pub fn name(self) -> Cow<'static, str> {
        Cow::Borrowed(match self {
            FilterStatKey::IpAddress => "ip-address",
            FilterStatKey::ReleaseVersion => "release-version",
            FilterStatKey::ErrorMessage => "error-message",
            FilterStatKey::BrowserExtensions => "browser-extensions",
            FilterStatKey::LegacyBrowsers => "legacy-browsers",
            FilterStatKey::Localhost => "localhost",
            FilterStatKey::WebCrawlers => "web-crawlers",
            FilterStatKey::InvalidCsp => "invalid-csp",
            FilterStatKey::FilteredTransactions => "filtered-transaction",
            FilterStatKey::DeniedName => "denied-name",
            FilterStatKey::DisabledNamespace => "disabled-namespace",
            FilterStatKey::GenericFilter(filter_identifier) => {
                return Cow::Owned(filter_identifier);
            }
        })
    }
}

impl fmt::Display for FilterStatKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.clone().name())
    }
}

impl<'a> TryFrom<&'a str> for FilterStatKey {
    type Error = &'a str;

    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
        Ok(match value {
            "ip-address" => FilterStatKey::IpAddress,
            "release-version" => FilterStatKey::ReleaseVersion,
            "error-message" => FilterStatKey::ErrorMessage,
            "browser-extensions" => FilterStatKey::BrowserExtensions,
            "legacy-browsers" => FilterStatKey::LegacyBrowsers,
            "localhost" => FilterStatKey::Localhost,
            "web-crawlers" => FilterStatKey::WebCrawlers,
            "invalid-csp" => FilterStatKey::InvalidCsp,
            "filtered-transaction" => FilterStatKey::FilteredTransactions,
            other => FilterStatKey::GenericFilter(other.to_string()),
        })
    }
}