// 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/. */

/// This file contains factory implementation information for the
/// JOG Runtime Registration module.
/// It is responsible for being able to build metrics and pings described at runtime.
/// It is generated to keep it in sync with how the runtime definitions are defined.

{% macro common_metric_data(metric_type) %}
CommonMetricData {
    {% for arg_name in common_metric_data_args if arg_name in metric_type.args %}
    {{ arg_name }},
    {% endfor %}
    ..Default::default()
}
{%- endmacro -%}

{% macro metric_ctor(metric_type_name, metric_type, ctor='new') %}
{{ metric_type_name|Camelize if not metric_type_name.startswith('labeled_') else "Labeled"}}Metric::{% if metric_type_name == 'event' %}with_runtime_extra_keys{% else %}{{ ctor }}{% endif %}(BaseMetricId(metric_id), meta
{%- for arg_name in metric_type.args if not metric_type_name.startswith('labeled_') and arg_name not in common_metric_data_args -%}
    , {{ arg_name }}.unwrap()
{%- endfor -%}
{%- if metric_type_name.startswith('labeled_') -%}
    , labels
{%- endif -%}
{%- if metric_type_name.startswith('dual_labeled_') -%}
    , keys, categories
{%- endif -%}
)
{%- endmacro -%}

use std::borrow::Cow;
use std::sync::atomic::{AtomicU32, Ordering};
use crate::private::{
    CommonMetricData,
    LabeledMetricData,
    Lifetime,
    MemoryUnit,
    TimeUnit,
    Ping,
    LabeledMetric,
    BaseMetricId,
{% for metric_type_name in metric_types.keys() if not metric_type_name.startswith('labeled_') %}
    {{ metric_type_name|Camelize }}Metric,
{% endfor %}};
use crate::private::traits::HistogramType;

pub(crate) static DYNAMIC_METRIC_BIT: u32 = {{runtime_metric_bit}};
// 2**DYNAMIC_METRIC_BIT + 1 (+1 because we reserve the 0 metric id)
static NEXT_METRIC_ID: AtomicU32 = AtomicU32::new({{2**runtime_metric_bit + 1}});
// Needed for the reset.
const FIRST_METRIC_ID: u32 = {{2**runtime_metric_bit + 1}};
#[cfg(feature = "with_gecko")] // only used in submit_ping_by_id, which is gecko-only.
pub(crate) static DYNAMIC_PING_BIT: u32 = {{runtime_ping_bit}};
// 2**DYNAMIC_PING_BIT + 1 (+1 because we reserve the 0 ping id)
static NEXT_PING_ID: AtomicU32 = AtomicU32::new({{2**runtime_ping_bit + 1}});
// Needed for the reset.
const FIRST_PING_ID: u32 = {{2**runtime_ping_bit + 1}};

#[inline(always)]
pub fn id_and_map_reset() {
  NEXT_METRIC_ID.store(FIRST_METRIC_ID, Ordering::SeqCst);
{% for metric_type_name, metric_type in metric_types.items() %}
  __jog_metric_maps::{{metric_type_name.upper()}}_MAP.write().unwrap().clear();
{% endfor %}

  NEXT_PING_ID.store(FIRST_PING_ID, Ordering::SeqCst);
  __jog_metric_maps::PING_MAP.write().unwrap().clear();
}

pub(crate) mod __jog_metric_maps {
    use crate::metrics::DynamicLabel;
    use crate::private::BaseMetricId;
    use crate::private::{
        Ping,
        LabeledMetric,
        NoExtraKeys,
        RuntimeObject,
    {% for metric_type_name in metric_types.keys() %}
        {{ metric_type_name|Camelize }}Metric,
    {% endfor %}
    };
    use once_cell::sync::Lazy;
    use std::collections::HashMap;
    use std::sync::{Arc, RwLock};

{% for metric_type_name in metric_types.keys() if metric_type_name != "event" and not metric_type_name.startswith('labeled_') and metric_type_name != "object" %}
    pub static {{ metric_type_name.upper() }}_MAP: Lazy<Arc<RwLock<HashMap<BaseMetricId, {{ metric_type_name|Camelize }}Metric>>>> =
        Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));

{% endfor %}
{# Labeled metrics are special because they're LabeledMetric<Labeled{Counter|Boolean|...}Metric> #}
{% for metric_type_name in metric_types.keys() if metric_type_name.startswith('labeled_') %}
    pub static {{ metric_type_name.upper() }}_MAP: Lazy<Arc<RwLock<HashMap<BaseMetricId, LabeledMetric<{{ metric_type_name|Camelize }}Metric, DynamicLabel>>>>> =
        Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));

{% endfor %}
    pub static PING_MAP: Lazy<Arc<RwLock<HashMap<u32, Ping>>>> =
        Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));

{# Event metrics are special because they're EventMetric<K> #}
    pub static EVENT_MAP: Lazy<Arc<RwLock<HashMap<BaseMetricId, EventMetric<NoExtraKeys>>>>> =
        Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));
{# Object metrics are special because they're ObjectMetric<K> #}
    #[allow(dead_code)]
    pub static OBJECT_MAP: Lazy<Arc<RwLock<HashMap<BaseMetricId, ObjectMetric<RuntimeObject>>>>> =
        Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));
}

#[derive(Debug)]
struct MetricTypeNotFoundError(String);
impl std::error::Error for MetricTypeNotFoundError {}
impl std::fmt::Display for MetricTypeNotFoundError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "Metric type {} not found", self.0)
    }
}

/// Creates and registers a metric, returning its type+id.
pub fn create_and_register_metric(
    metric_type: &str,
{# The rest of these are handrolled because it proved easier than maintaining a
map of argument name to argument type. I may regret this if I need it again. #}
{# In order of util.common_metric_args and util.extra_metric_args, because why not. #}
    category: String,
    name: String,
    send_in_pings: Vec<String>,
    lifetime: Lifetime,
    disabled: bool,
    in_session: bool,
    time_unit: Option<TimeUnit>,
    memory_unit: Option<MemoryUnit>,
    allowed_extra_keys: Option<Vec<String>>,
{# Skipping reason_codes since that's a ping thing. #}
    range_min: Option<i64>,
    range_max: Option<i64>,
    bucket_count: Option<i64>,
    histogram_type: Option<HistogramType>,
    numerators: Option<Vec<CommonMetricData>>,
{# And, don't forget the list of acceptable labels for a labeled metric. #}
    labels: Option<Vec<Cow<'static, str>>>,
    permit_non_commutative_operations_over_ipc: Option<bool>,
    keys: Option<Vec<Cow<'static, str>>>,
    categories: Option<Vec<Cow<'static, str>>>,
) -> Result<(u32, u32), Box<dyn std::error::Error>> {
    let metric_id = NEXT_METRIC_ID.fetch_add(1, Ordering::SeqCst);
    let metric32 = match metric_type {
{% for metric_type_name, metric_type in metric_types.items() %}
        "{{ metric_type_name }}" => {
            let meta ={{" "}}
                {%- if metric_type_name == "labeled_custom_distribution" -%}
                LabeledMetricData::CustomDistribution {
                    cmd: {{ common_metric_data(metric_type)|indent(20) }}
                    {%- for arg_name in metric_type.args if arg_name not in common_metric_data_args -%}
                        , {{ arg_name }}: {{ arg_name }}.unwrap()
                    {%- endfor -%}
                };
                {%- elif metric_type_name == "labeled_memory_distribution" -%}
                LabeledMetricData::MemoryDistribution {
                    cmd: {{ common_metric_data(metric_type)|indent(20) }}
                    {%- for arg_name in metric_type.args if arg_name not in common_metric_data_args -%}
                        , {{ "unit" if arg_name == "memory_unit" else arg_name }}: {{ arg_name }}.unwrap()
                    {%- endfor -%}
                };
                {%- elif metric_type_name == "labeled_timing_distribution" -%}
                LabeledMetricData::TimingDistribution {
                    cmd: {{ common_metric_data(metric_type)|indent(20) }}
                    {%- for arg_name in metric_type.args if arg_name not in common_metric_data_args -%}
                        , {{ "unit" if arg_name == "time_unit" else arg_name }}: {{ arg_name }}.unwrap()
                    {%- endfor -%}
                };
                {%- elif metric_type_name.startswith('labeled_') -%}
                LabeledMetricData::Common {
                    cmd: {{ common_metric_data(metric_type)|indent(16) }}
                };
                {%- else -%}
                {{ common_metric_data(metric_type)|indent(12) }};
                {% endif %}
            {% if sometimes_supports_noncommutative_operations(metric_type_name) %}
            let metric = if permit_non_commutative_operations_over_ipc.unwrap_or(false) {
                {{ metric_ctor(metric_type_name, metric_type, ctor="with_unordered_ipc") }}
            } else {
                {{ metric_ctor(metric_type_name, metric_type) }}
            };
            {% else %}
            let metric = {{ metric_ctor(metric_type_name, metric_type) }};
            {% endif %}
            let metric32: u32 = ({{metric_type.id}} << {{ID_BITS}}) | metric_id;
            assert!(
                __jog_metric_maps::{{metric_type_name.upper()}}_MAP.write()?.insert(BaseMetricId(metric_id), metric).is_none(),
                "We should never insert a runtime metric with an already-used id."
            );
            metric32
        }
{% endfor %}
        _ => return Err(Box::new(MetricTypeNotFoundError(metric_type.to_string())))
    };
    Ok((metric32, metric_id))
}

/// Creates and registers a ping, returning its id.
pub fn create_and_register_ping(
    ping_name: String,
    include_client_id: bool,
    send_if_empty: bool,
    precise_timestamps: bool,
    include_info_sections: bool,
    enabled: bool,
    schedules_pings: Vec<String>,
    reason_codes: Vec<String>,
    follows_collection_enabled: bool,
    uploader_capabilities: Vec<String>,
) -> Result<u32, Box<dyn std::error::Error>> {
    let ping_id = NEXT_PING_ID.fetch_add(1, Ordering::SeqCst);
    let ping = Ping::new(ping_name, include_client_id, send_if_empty, precise_timestamps, include_info_sections, enabled, schedules_pings, reason_codes, follows_collection_enabled, uploader_capabilities);
    assert!(
        __jog_metric_maps::PING_MAP.write()?.insert(ping_id.into(), ping).is_none(),
        "We should never insert a runtime ping with an already-used id."
    );
    Ok(ping_id)
}
