1use std::fmt::Display;
6use std::sync::atomic::{AtomicU8, Ordering};
7
8use malloc_size_of_derive::MallocSizeOf;
9
10#[cfg(feature = "sqlite")]
11use rusqlite::Transaction;
12
13use crate::error::{Error, ErrorKind};
14#[cfg(feature = "sqlite")]
15use crate::error_recording::record_error_sqlite;
16
17#[cfg(feature = "sqlite")]
18use crate::metrics::{
19 dual_labeled_counter::validate_dual_label_sqlite, labeled::validate_dynamic_label_sqlite,
20};
21
22#[cfg(not(feature = "sqlite"))]
23use crate::metrics::{
24 dual_labeled_counter::validate_dual_label_rkv,
25 labeled::{combine_base_identifier_and_label, validate_dynamic_label_rkv},
26};
27#[cfg(feature = "sqlite")]
28use crate::ErrorType;
29use crate::Glean;
30use serde::{Deserialize, Serialize};
31
32#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default, MallocSizeOf)]
36#[repr(i32)] #[serde(rename_all = "lowercase")]
38pub enum Lifetime {
39 #[default]
41 Ping,
42 Application,
44 User,
46}
47
48impl Lifetime {
49 pub fn as_str(self) -> &'static str {
51 match self {
52 Lifetime::Ping => "ping",
53 Lifetime::Application => "app",
54 Lifetime::User => "user",
55 }
56 }
57}
58
59impl TryFrom<i32> for Lifetime {
60 type Error = Error;
61
62 fn try_from(value: i32) -> Result<Lifetime, Self::Error> {
63 match value {
64 0 => Ok(Lifetime::Ping),
65 1 => Ok(Lifetime::Application),
66 2 => Ok(Lifetime::User),
67 e => Err(ErrorKind::Lifetime(e).into()),
68 }
69 }
70}
71
72#[derive(Default, Debug, Clone, Deserialize, Serialize, MallocSizeOf)]
74pub struct CommonMetricData {
75 pub name: String,
77 pub category: String,
79 pub send_in_pings: Vec<String>,
81 pub lifetime: Lifetime,
83 pub disabled: bool,
87 pub in_session: bool,
94
95 pub label: Option<MetricLabel>,
103}
104
105#[derive(Debug, Clone, Deserialize, Serialize, MallocSizeOf, uniffi::Enum)]
108pub enum MetricLabel {
109 Static(String),
111 Label(String),
113 KeyOnly(String, String),
115 CategoryOnly(String, String),
117 KeyAndCategory(String, String),
119}
120
121impl Default for MetricLabel {
122 fn default() -> Self {
123 Self::Label(String::new())
124 }
125}
126
127impl Display for MetricLabel {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 use crate::metrics::dual_labeled_counter::RECORD_SEPARATOR;
130 match self {
131 MetricLabel::Static(label) | MetricLabel::Label(label) => write!(f, "{label}"),
132 MetricLabel::KeyOnly(key, category)
133 | MetricLabel::CategoryOnly(key, category)
134 | MetricLabel::KeyAndCategory(key, category) => {
135 write!(f, "{key}{RECORD_SEPARATOR}{category}")
136 }
137 }
138 }
139}
140
141#[derive(Default, Debug, MallocSizeOf)]
142pub struct CommonMetricDataInternal {
143 pub inner: CommonMetricData,
144 pub disabled: AtomicU8,
145}
146
147impl Clone for CommonMetricDataInternal {
148 fn clone(&self) -> Self {
149 Self {
150 inner: self.inner.clone(),
151 disabled: AtomicU8::new(self.disabled.load(Ordering::Relaxed)),
152 }
153 }
154}
155
156impl From<CommonMetricData> for CommonMetricDataInternal {
157 fn from(input_data: CommonMetricData) -> Self {
158 let disabled = input_data.disabled;
159 Self {
160 inner: input_data,
161 disabled: AtomicU8::new(u8::from(disabled)),
162 }
163 }
164}
165
166#[cfg(feature = "sqlite")]
168pub enum LabelCheck {
169 NoLabel,
171 Label(String),
173 Error(String, i32),
176}
177
178#[cfg(feature = "sqlite")]
179impl LabelCheck {
180 pub fn label(&self) -> &str {
182 use LabelCheck::*;
183 match self {
184 NoLabel => "",
185 Label(label) | Error(label, _) => label,
186 }
187 }
188
189 pub fn record_error(
191 &self,
192 glean: &Glean,
193 tx: &mut Transaction,
194 metric_name: &str,
195 send_in_pings: &[String],
196 ) {
197 let LabelCheck::Error(_, count) = self else {
198 return;
199 };
200
201 #[cfg(feature = "sqlite")]
202 record_error_sqlite(
203 glean,
204 tx,
205 metric_name,
206 send_in_pings,
207 ErrorType::InvalidLabel,
208 *count,
209 );
210
211 #[cfg(not(feature = "sqlite"))]
212 todo!()
213 }
214
215 fn map(self, mut f: impl FnMut(String) -> String) -> Self {
220 use LabelCheck::*;
221
222 match self {
223 NoLabel => NoLabel,
224 Label(s) => Label(f(s)),
225 Error(s, cnt) => Error(f(s), cnt),
226 }
227 }
228}
229
230impl CommonMetricDataInternal {
231 pub fn new<A: Into<String>, B: Into<String>, C: Into<String>>(
233 category: A,
234 name: B,
235 ping_name: C,
236 ) -> CommonMetricDataInternal {
237 CommonMetricDataInternal {
238 inner: CommonMetricData {
239 name: name.into(),
240 category: category.into(),
241 send_in_pings: vec![ping_name.into()],
242 ..Default::default()
243 },
244 disabled: AtomicU8::new(0),
245 }
246 }
247
248 pub(crate) fn base_identifier(&self) -> String {
253 if self.inner.category.is_empty() {
254 self.inner.name.clone()
255 } else {
256 format!("{}.{}", self.inner.category, self.inner.name)
257 }
258 }
259
260 #[cfg(not(feature = "sqlite"))]
265 pub(crate) fn identifier(&self, glean: &Glean, record: bool) -> String {
266 let base_identifier = self.base_identifier();
267
268 if let Some(label) = &self.inner.label {
269 let label = match label {
270 MetricLabel::Static(label) => label.to_string(),
271 MetricLabel::Label(label) => {
272 validate_dynamic_label_rkv(glean, self, &base_identifier, label, record)
273 }
274 MetricLabel::KeyOnly(..) => {
275 validate_dual_label_rkv(glean, self, &base_identifier, label, record)
276 }
277 MetricLabel::CategoryOnly(..) => {
278 validate_dual_label_rkv(glean, self, &base_identifier, label, record)
279 }
280 MetricLabel::KeyAndCategory(..) => {
281 validate_dual_label_rkv(glean, self, &base_identifier, label, record)
282 }
283 };
284
285 combine_base_identifier_and_label(&base_identifier, &label)
286 } else {
287 base_identifier
288 }
289 }
290
291 #[cfg(feature = "sqlite")]
298 pub(crate) fn check_labels(&self, tx: &rusqlite::Connection) -> LabelCheck {
299 let base_identifier = self.base_identifier();
300
301 if let Some(label) = &self.inner.label {
302 match label {
303 MetricLabel::Static(label) => LabelCheck::Label(label.to_string()),
304 MetricLabel::Label(label) => {
305 validate_dynamic_label_sqlite(tx, &base_identifier, label)
306 }
307 MetricLabel::KeyOnly(key, static_category) => {
308 validate_dual_label_sqlite(tx, &base_identifier, key, "")
309 .map(|key| format!("{key}{static_category}"))
310 }
311 MetricLabel::CategoryOnly(static_key, category) => {
312 validate_dual_label_sqlite(tx, &base_identifier, "", category)
313 .map(|category| format!("{static_key}{category}"))
314 }
315 MetricLabel::KeyAndCategory(key, category) => {
316 validate_dual_label_sqlite(tx, &base_identifier, key, category)
317 }
318 }
319 } else {
320 LabelCheck::NoLabel
321 }
322 }
323
324 pub fn in_session(&self) -> bool {
330 self.inner.in_session
331 }
332
333 pub fn storage_names(&self) -> &[String] {
335 &self.inner.send_in_pings
336 }
337}