// -*- mode: Rust -*-

// AUTOGENERATED BY glean_parser.  DO NOT EDIT.
{# The rendered source is autogenerated, but this
Jinja2 template is not. Please file bugs! #}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

{% macro generate_extra_keys(obj) %}
{% for name, _ in obj["_generate_enums"] %}
{# we always use the `extra` suffix, because we only expose the new event API #}
{% set suffix = "Extra" %}
{% if obj|attr(name)|length %}
    {% if obj.has_extra_types %}
    {{ extra_keys_with_types(obj, name, suffix)|indent }}
    {% else %}
    compile_error!("Untyped event extras not supported. Please annotate event extras with a type. See documentation for details. (Metric: {{obj.category}}.{{obj.name}}, defined in: {{obj.defined_in['filepath']}}:{{obj.defined_in['line']}})");
    {% endif %}
{% endif %}
{% endfor %}
{% endmacro %}
{% macro extra_keys_with_types(obj, name, suffix) %}
#[derive(Default, Debug, Clone, Hash, Eq, PartialEq)]
pub struct {{ obj.name|Camelize }}{{ suffix }} {
    {% for item, type in obj|attr(name) %}
    pub {{ item|snake_case }}: Option<{{type|extra_type_name}}>,
    {% endfor %}
}

impl ExtraKeys for {{ obj.name|Camelize }}{{ suffix }} {
    const ALLOWED_KEYS: &'static [&'static str] = {{ obj.allowed_extra_keys|extra_keys }};

    fn into_ffi_extra(self) -> ::std::collections::HashMap<i32, String> {
        let mut map = ::std::collections::HashMap::new();
        {% for key, _ in obj|attr(name) %}
        self.{{key|snake_case}}.and_then(|val| map.insert({{loop.index-1}}, val));
        {% endfor %}
        map
    }
}
{% endmacro %}
{% for category in categories %}
{% if category.contains_pings %}
use glean::private::Ping;
use once_cell::sync::Lazy;
{% for obj in category.objs.values() %}

#[allow(non_upper_case_globals, dead_code)]
/// {{ obj.description|wordwrap() | replace('\n', '\n/// ') }}
#[rustfmt::skip]
pub static {{ obj.name|snake_case }}: Lazy<Ping> =
    Lazy::new(|| Ping::new("{{ obj.name }}", {{ obj.include_client_id|rust }}, {{ obj.send_if_empty|rust }}, {{ obj.reason_codes|rust }}));
{% endfor %}
{% else %}
pub mod {{ category.name|snake_case }} {
    #[allow(unused_imports)] // HistogramType might be unusued, let's avoid warnings
    use glean::{private::*, traits::ExtraKeys, CommonMetricData, HistogramType, Lifetime};
    use once_cell::sync::Lazy;
    {% for obj in category.objs.values() %}

    {% if obj|attr("_generate_enums") %}
{{ generate_extra_keys(obj) }}
    {%- endif %}
    #[allow(non_upper_case_globals, dead_code)]
    /// generated from {{ category.name }}.{{ obj.name }}
    ///
    /// {{ obj.description|wordwrap() | replace('\n', '\n    /// ') }}
    pub static {{ obj.name|snake_case }}: Lazy<{{ obj|type_name }}> = Lazy::new(|| {
        {{ obj|ctor }}(CommonMetricData {
            category: {{ obj.category|rust }},
            name: {{ obj.name|rust }},
            send_in_pings: {{ obj.send_in_pings|rust }},
            lifetime: {{ obj.lifetime|rust }},
            disabled: {{ obj.is_disabled()|rust }},
            ..Default::default()
        }{{ ", " if obj.labeled else ")\n" }}
        {%- if obj.labeled -%}
        {%- if obj.labels -%}
        Some({{ obj.labels|rust }})
        {%- else -%}
        None
        {%- endif -%})
        {% endif %}
    });
    {% endfor %}
}
{% endif %}
{% endfor %}
{% if metric_by_type|length > 0 %}

#[allow(dead_code)]
pub(crate) mod __glean_metric_maps {
    use std::collections::HashMap;

    use super::{id_for_extra_key, extra_keys_len};
    use crate::private::*;
    use once_cell::sync::Lazy;

{% for typ, metrics in metric_by_type.items() %}
    pub static {{typ.0}}: Lazy<HashMap<MetricId, &Lazy<{{typ.1}}>>> = Lazy::new(|| {
        let mut map = HashMap::with_capacity({{metrics|length}});
        {% for metric in metrics %}
        map.insert({{metric.0}}.into(), &super::{{metric.1}});
        {% endfor %}
        map
    });

{% endfor %}

    /// Wrapper to record an event based on its metric ID.
    ///
    /// # Arguments
    ///
    /// * `metric_id` - The metric's ID to look up
    /// * `extra`     - An map of (extra key id, string) pairs.
    ///                 The map will be decoded into the appropriate `ExtraKeys` type.
    /// # Returns
    ///
    /// Returns `Ok(())` if the event was found and `record` was called with the given `extra`,
    /// or an `EventRecordingError::InvalidId` if no event by that ID exists
    /// or an `EventRecordingError::InvalidExtraKey` if the `extra` map could not be deserialized.
    pub(crate) fn record_event_by_id(metric_id: u32, extra: HashMap<i32, String>) -> Result<(), EventRecordingError> {
        match metric_id {
{% for metric_id, event in events_by_id.items() %}
          {{metric_id}} => {
              assert!(
                  extra_keys_len(&super::{{event}}) != 0 || extra.is_empty(),
                  "No extra keys allowed, but some were passed"
              );

              super::{{event}}.record_raw(extra);
              Ok(())
          }
{% endfor %}
           _ => Err(EventRecordingError::InvalidId),
        }
    }

    /// Wrapper to record an event based on its metric ID, with a provided timestamp.
    ///
    /// # Arguments
    ///
    /// * `metric_id` - The metric's ID to look up
    /// * `timestamp` - The time at which this event was recorded.
    /// * `extra`     - An map of (extra key id, string) pairs.
    ///                 The map will be decoded into the appropriate `ExtraKeys` type.
    /// # Returns
    ///
    /// Returns `Ok(())` if the event was found and `record` was called with the given `extra`,
    /// or an `EventRecordingError::InvalidId` if no event by that ID exists
    /// or an `EventRecordingError::InvalidExtraKey` if the event doesn't take extra pairs,
    /// but some are passed in.
    pub(crate) fn record_event_by_id_with_time(metric_id: MetricId, timestamp: u64, extra: HashMap<i32, String>) -> Result<(), EventRecordingError> {
        match metric_id {
{% for metric_id, event in events_by_id.items() %}
          MetricId({{metric_id}}) => {
              if extra_keys_len(&super::{{event}}) == 0 && !extra.is_empty() {
                return Err(EventRecordingError::InvalidExtraKey);
              }

              super::{{event}}.record_with_time(timestamp, extra);
              Ok(())
          }
{% endfor %}
           _ => Err(EventRecordingError::InvalidId),
        }
    }

    /// Wrapper to record an event based on its metric ID.
    ///
    /// # Arguments
    ///
    /// * `metric_id` - The metric's ID to look up
    /// * `extra`     - An map of (string, string) pairs.
    ///                 The map will be decoded into the appropriate `ExtraKeys` types.
    /// # Returns
    ///
    /// Returns `Ok(())` if the event was found and `record` was called with the given `extra`,
    /// or an `EventRecordingError::InvalidId` if no event by that ID exists
    /// or an `EventRecordingError::InvalidExtraKey` if the `extra` map could not be deserialized.
    pub(crate) fn record_event_by_id_with_strings(metric_id: u32, extra: HashMap<String, String>) -> Result<(), EventRecordingError> {
        match metric_id {
{% for metric_id, event in events_by_id.items() %}
          {{metric_id}} => {
              assert!(
                  extra_keys_len(&super::{{event}}) != 0 || extra.is_empty(),
                  "No extra keys allowed, but some were passed"
              );

              let extra = extra
                  .into_iter()
                  .map(|(k, v)| id_for_extra_key(&*k, &super::{{event}}).map(|k| (k, v)))
                  .collect::<Result<HashMap<_, _>, _>>()?;
              super::{{event}}.record_raw(extra);
              Ok(())
          }
{% endfor %}
           _ => Err(EventRecordingError::InvalidId),
        }
    }

    /// Wrapper to get the currently stored events for event metric.
    ///
    /// # Arguments
    ///
    /// * `metric_id` - The metric's ID to look up
    /// * `ping_name` - (Optional) The ping name to look into.
    ///                 Defaults to the first value in `send_in_pings`.
    ///
    /// # Returns
    ///
    /// Returns the recorded events or `None` if nothing stored.
    ///
    /// # Panics
    ///
    /// Panics if no event by the given metric ID could be found.
    pub(crate) fn event_test_get_value_wrapper(metric_id: u32, ping_name: Option<String>) -> Option<Vec<RecordedEvent>> {
        match metric_id {
{% for metric_id, event in events_by_id.items() %}
           {{metric_id}} => super::{{event}}.test_get_value(ping_name.as_deref()),
{% endfor %}
           _ => panic!("No event for metric id {}", metric_id),
        }
    }

    /// Check the provided event for errors.
    ///
    /// # Arguments
    ///
    /// * `metric_id` - The metric's ID to look up
    /// * `ping_name` - (Optional) The ping name to look into.
    ///                 Defaults to the first value in `send_in_pings`.
    ///
    /// # Returns
    ///
    /// Returns a string for the recorded error or `None`.
    ///
    /// # Panics
    ///
    /// Panics if no event by the given metric ID could be found.
    #[allow(unused_variables)]
    pub(crate) fn event_test_get_error(metric_id: u32, ping_name: Option<String>) -> Option<String> {
        #[cfg(feature = "with_gecko")]
        match metric_id {
{% for metric_id, event in events_by_id.items() %}
           {{metric_id}} => test_get_errors_string!(super::{{event}}, ping_name),
{% endfor %}
           _ => panic!("No event for metric id {}", metric_id),
        }

        #[cfg(not(feature = "with_gecko"))]
        {
            return None;
        }
    }

    pub(crate) mod submetric_maps {
        use std::sync::{
          atomic::AtomicU32,
          RwLock,
        };
        use super::*;

        pub(crate) const MIN_LABELED_SUBMETRIC_ID: u32 = {{min_submetric_id}};
        pub(crate) static NEXT_LABELED_SUBMETRIC_ID: AtomicU32 = AtomicU32::new(MIN_LABELED_SUBMETRIC_ID);
        pub(crate) static LABELED_METRICS_TO_IDS: Lazy<RwLock<HashMap<(u32, String), u32>>> = Lazy::new(||
            RwLock::new(HashMap::new())
        );

{% for typ, metrics in metric_by_type.items() %}
{% if typ.0 in ('BOOLEAN_MAP', 'COUNTER_MAP', 'STRING_MAP') %}
        pub static {{typ.0}}: Lazy<RwLock<HashMap<MetricId, Labeled{{typ.1}}>>> = Lazy::new(||
            RwLock::new(HashMap::new())
        );
{% endif %}
{% endfor%}
    }
}
{% endif %}