summaryrefslogtreecommitdiffstats
path: root/toolkit/components/glean/build_scripts/glean_parser_ext/cpp.py
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/glean/build_scripts/glean_parser_ext/cpp.py')
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/cpp.py139
1 files changed, 139 insertions, 0 deletions
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/cpp.py b/toolkit/components/glean/build_scripts/glean_parser_ext/cpp.py
new file mode 100644
index 0000000000..003ae4997c
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/cpp.py
@@ -0,0 +1,139 @@
+# -*- coding: utf-8 -*-
+
+# 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/.
+
+"""
+Outputter to generate C++ code for metrics.
+"""
+
+import json
+
+import jinja2
+from glean_parser import metrics, util
+from util import generate_metric_ids, generate_ping_ids, get_metrics
+
+
+def cpp_datatypes_filter(value):
+ """
+ A Jinja2 filter that renders C++ literals.
+
+ Based on Python's JSONEncoder, but overrides:
+ - lists to array literals {}
+ - strings to "value"
+ """
+
+ class CppEncoder(json.JSONEncoder):
+ def iterencode(self, value):
+ if isinstance(value, list):
+ yield "{"
+ first = True
+ for subvalue in list(value):
+ if not first:
+ yield ", "
+ yield from self.iterencode(subvalue)
+ first = False
+ yield "}"
+ elif isinstance(value, str):
+ yield '"' + value + '"'
+ else:
+ yield from super().iterencode(value)
+
+ return "".join(CppEncoder().iterencode(value))
+
+
+def type_name(obj):
+ """
+ Returns the C++ type to use for a given metric object.
+ """
+
+ if getattr(obj, "labeled", False):
+ class_name = util.Camelize(obj.type[8:]) # strips "labeled_" off the front.
+ label_enum = "DynamicLabel"
+ if obj.labels and len(obj.labels):
+ label_enum = f"{util.Camelize(obj.name)}Label"
+ return f"Labeled<impl::{class_name}Metric, {label_enum}>"
+ generate_enums = getattr(obj, "_generate_enums", []) # Extra Keys? Reasons?
+ if len(generate_enums):
+ for name, _ in generate_enums:
+ if not len(getattr(obj, name)) and isinstance(obj, metrics.Event):
+ return util.Camelize(obj.type) + "Metric<NoExtraKeys>"
+ else:
+ # we always use the `extra` suffix,
+ # because we only expose the new event API
+ suffix = "Extra"
+ return "{}Metric<{}>".format(
+ util.Camelize(obj.type), util.Camelize(obj.name) + suffix
+ )
+ return util.Camelize(obj.type) + "Metric"
+
+
+def extra_type_name(typ: str) -> str:
+ """
+ Returns the corresponding Rust type for event's extra key types.
+ """
+
+ if typ == "boolean":
+ return "bool"
+ elif typ == "string":
+ return "nsCString"
+ elif typ == "quantity":
+ return "uint32_t"
+ else:
+ return "UNSUPPORTED"
+
+
+def output_cpp(objs, output_fd, options={}):
+ """
+ Given a tree of objects, output C++ code to the file-like object `output_fd`.
+
+ :param objs: A tree of objects (metrics and pings) as returned from
+ `parser.parse_objects`.
+ :param output_fd: Writeable file to write the output to.
+ :param options: options dictionary.
+ """
+
+ # Monkeypatch util.snake_case for the templates to use
+ util.snake_case = lambda value: value.replace(".", "_").replace("-", "_")
+ # Monkeypatch util.get_jinja2_template to find templates nearby
+
+ def get_local_template(template_name, filters=()):
+ env = jinja2.Environment(
+ loader=jinja2.PackageLoader("cpp", "templates"),
+ trim_blocks=True,
+ lstrip_blocks=True,
+ )
+ env.filters["camelize"] = util.camelize
+ env.filters["Camelize"] = util.Camelize
+ for filter_name, filter_func in filters:
+ env.filters[filter_name] = filter_func
+ return env.get_template(template_name)
+
+ util.get_jinja2_template = get_local_template
+ get_metric_id = generate_metric_ids(objs)
+ get_ping_id = generate_ping_ids(objs)
+
+ if "pings" in objs:
+ template_filename = "cpp_pings.jinja2"
+ if objs.get("tags"):
+ del objs["tags"]
+ else:
+ template_filename = "cpp.jinja2"
+ objs = get_metrics(objs)
+
+ template = util.get_jinja2_template(
+ template_filename,
+ filters=(
+ ("cpp", cpp_datatypes_filter),
+ ("snake_case", util.snake_case),
+ ("type_name", type_name),
+ ("extra_type_name", extra_type_name),
+ ("metric_id", get_metric_id),
+ ("ping_id", get_ping_id),
+ ("Camelize", util.Camelize),
+ ),
+ )
+
+ output_fd.write(template.render(all_objs=objs))
+ output_fd.write("\n")