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

#ifndef mozilla_Glean{{ options.header_name }}_h
#define mozilla_Glean{{ options.header_name }}_h

{% if all_objs|has_structure %}
#include "mozilla/JSONStringWriteFuncs.h"
{% endif %}
{% if options.standalone %}
#include "mozilla/glean/bindings/MetricStandaloneTypes.h"
{% else %}
#include "mozilla/glean/bindings/MetricTypes.h"
{% endif %}

namespace mozilla::glean {

{%- macro structure_to_json(struct, property_name, value) -%}
{% if struct.type == "array" and property_name %}
if ({{value}}.isSome()) {
  writer.StartArrayProperty("{{property_name}}");
  {% set valuename = value|replace("->", "")|camelize ~ "Item" %}
  for (const auto& {{valuename}}: *{{value}}) {
    {{ structure_to_json(struct["items"], None, valuename)|indent(4) }}
  }
  writer.EndArray();
}
{% elif struct.type == "array" and not property_name %}
writer.StartArrayElement();
{
  {% set valuename = value|replace("->", "")|camelize ~ "Item" %}
  for (const auto& {{valuename}}: {{value}}) {
    {{ structure_to_json(struct["items"], None, valuename)|indent(4) }}
  }
}
writer.EndArray();
{% elif struct.type == "object" and property_name %}
if ({{value}}.isSome()) {
  writer.StartObjectProperty("{{property_name}}");
{% for propname, val in struct.properties.items() %}
  {{ structure_to_json(val, propname, value ~ "->" ~ propname)|indent(2) }}
{%- endfor %}
  writer.EndObject();
}
{% elif struct.type == "object" and not property_name %}
writer.StartObjectElement();
{
{% for propname, val in struct.properties.items() %}
  {{ structure_to_json(val, propname, value ~ "." ~ propname)|indent(2) }}
{%- endfor %}
}
writer.EndObject();
{% elif property_name %}
if ({{value}}.isSome()) {
  writer.{{struct.type|jsonwriter_prefix}}Property("{{property_name}}", *({{value}}));
}
{% else %}
writer.{{struct.type|jsonwriter_prefix}}Element({{value}});
{% endif %}
{%- endmacro -%}

{%- macro generate_structure(name, struct) -%}
{% if struct.type == "array" %}
{% if struct["items"].type not in ("array", "object") %}
  using {{ name }} = nsTArray<{{struct["items"].type|structure_type_name}}>;
{% else %}
{{ generate_structure(name ~ "Item", struct["items"]) }}
  using {{ name }} = nsTArray<{{ name }}Item>;
{% endif %}
{% elif struct.type == "object" %}
{% for itemname, val in struct.properties.items() %}
{% if val.type in ("array", "object") %}
{{ generate_structure(name ~ itemname|Camelize, val) }}
{% endif %}
{% endfor %}

  struct {{ name }} {
  {% for itemname, val in struct.properties.items() %}
  {% if val.type not in ("array", "object") %}
    Maybe<{{val.type|structure_type_name}}> {{ itemname }};
  {% else %}
    Maybe<{{name ~ itemname|Camelize}}> {{ itemname }};
  {% endif %}
  {% endfor %}
  };
{% endif %}
{%- endmacro -%}

{%- macro generate_extra_keys(obj) -%}
{% for name, suffix 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 %}
  {{ extra_keys_with_types(obj, name, suffix)|indent(2) }}
{% endif %}
{% endfor %}
{%- endmacro -%}

{%- macro extra_keys_with_types(obj, name, suffix) -%}
struct {{ obj.name|Camelize }}{{ suffix }} {
  {% for item, type in obj|attr(name) %}
  mozilla::Maybe<{{type|extra_type_name}}> {{ item|camelize }};
  {% endfor %}

  std::tuple<nsTArray<nsCString>, nsTArray<nsCString>> ToFfiExtra() const {
    nsTArray<nsCString> extraKeys;
    nsTArray<nsCString> extraValues;
    {% for item, type in obj|attr(name) %}
    if ({{item|camelize}}) {
      extraKeys.AppendElement()->AssignASCII("{{item}}");
      {% if type == "string" %}
      extraValues.EmplaceBack({{item|camelize}}.value());
      {% elif type == "boolean" %}
      extraValues.AppendElement()->AssignASCII({{item|camelize}}.value() ? "true" : "false");
      {% elif type == "quantity" %}
      extraValues.AppendElement()->AppendInt({{item|camelize}}.value());
      {% else %}
#error "Glean: Invalid extra key type for metric {{obj.category}}.{{obj.name}}, defined in: {{obj.defined_in['filepath']}}:{{obj.defined_in['line']}})"
      {% endif %}
    }
    {% endfor %}
    return std::make_tuple(std::move(extraKeys), std::move(extraValues));
  }
};
{%- endmacro -%}

{# Okay, so though `enum class` means we can't pollute the namespace with the
   enum variants' identifiers, there's no guarantee there isn't some
   preprocessor #define lying in wait to collide with us. Using CamelCase
   helps, but isn't foolproof (X11/X.h has `#define Success 0`).
   So we prefix it. I chose `e` (for `enum`) for the prefix. #}
{%- macro generate_label_enum(obj) -%}
enum class {{ obj.name|Camelize }}Label: uint16_t {
  {% for label in obj.ordered_labels %}
  e{{ label|Camelize }} = {{loop.index0}},
  {% endfor %}
  e__Other__,
};
{%- endmacro %}

{% for category_name, objs in all_objs.items() %}

namespace {{ category_name|snake_case }} {
  {% for obj in objs.values() %}
  /**
   * generated from {{ category_name }}.{{ obj.name }}
  {% if obj|attr("_generate_enums") or (obj.labeled and obj.labels and obj.labels|length) or obj|attr("_generate_structure")%}
   */
  {% if obj|attr("_generate_structure") %}
{{ generate_structure(obj.name|Camelize ~ "Object", obj._generate_structure) }}
  {%- endif %}
  {% if obj|attr("_generate_enums") %}
{{ generate_extra_keys(obj) }}
  {%- endif %}
  {% if obj.labeled and obj.labels and obj.labels|length %}
  {{ generate_label_enum(obj)|indent(2) }}
  {% endif %}
  /**
  {% endif %}
   * {{ obj.description|trim | replace('\n', '\n   * ') }}
   */
  constexpr impl::{{ obj|type_name }} {{obj.name|snake_case }}({{obj|metric_id}});
  {% if not loop.last %}

  {% endif %}
  {% endfor %}
}
{% for obj in objs.values() %}
{% if obj|attr("_generate_structure") %}
{% set typename_t = category_name|snake_case ~ "::" ~ obj.name|Camelize ~ "Object" %}

template <>
inline void impl::ObjectMetric<{{typename_t}}, {{typename_t}}Tag>::Set(const {{typename_t}}& aObj) const {
  nsCString json;
  JSONStringRefWriteFunc writeFunc(json);
  JSONWriter writer(writeFunc, JSONWriter::CollectionStyle::SingleLineStyle);

  {{ structure_to_json(obj._generate_structure, None, "aObj")|indent(2) }}

  SetStr(json);
}
{% endif %}
{% endfor %}
{% endfor %}

} // namespace mozilla::glean

#endif // mozilla_Glean{{ options.header_name }}_h
