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 a util.snake_case function 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 len(objs) == 1 and "pings" in objs: template_filename = "cpp_pings.jinja2" else: template_filename = "cpp.jinja2" template = util.get_jinja2_template( template_filename, filters=( ("cpp", cpp_datatypes_filter), ("snake_case", util.snake_case), ("type_name", type_name), ("metric_id", get_metric_id), ("ping_id", get_ping_id), ("is_implemented_type", is_implemented_metric_type), ("Camelize", util.Camelize), ), ) output_fd.write(template.render(all_objs=objs)) output_fd.write("\n")
def write_pings(objs, output_fd, template_filename): """ Given a tree of objects `objs`, output pings-only code for the JS API to the file-like object `output_fd` using template `template_filename` """ template = util.get_jinja2_template( template_filename, filters=(), ) ping_string_table = StringTable() get_ping_id = generate_ping_ids(objs) # The map of a ping's name to its entry (a combination of a monotonic # integer and its index in the string table) pings = {} for ping_name in objs["pings"].keys(): ping_id = get_ping_id(ping_name) ping_name = util.camelize(ping_name) pings[ping_name] = ping_entry(ping_id, ping_string_table.stringIndex(ping_name)) ping_map = [(bytearray(ping_name, "ascii"), ping_entry) for (ping_name, ping_entry) in pings.items()] ping_string_table = ping_string_table.writeToString("gPingStringTable") ping_phf = PerfectHash(ping_map, 64) ping_by_name_lookup = ping_phf.cxx_codegen( name="PingByNameLookup", entry_type="ping_entry_t", lower_entry=lambda x: str(x[1]), key_type="const nsACString&", key_bytes="aKey.BeginReading()", key_length="aKey.Length()", return_type="static Maybe<uint32_t>", return_entry="return ping_result_check(aKey, entry);", ) output_fd.write( template.render( ping_index_bits=PING_INDEX_BITS, ping_by_name_lookup=ping_by_name_lookup, ping_string_table=ping_string_table, )) output_fd.write("\n")
def output_rust(objs, output_fd, options={}): """ Given a tree of objects, output Rust 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, presently unused. """ # Monkeypatch a util.snake_case function 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("rust", "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) # Map from a tuple (const, typ) to an array of tuples (id, path) # where: # const: The Rust constant name to be used for the lookup map # typ: The metric type to be stored in the lookup map # id: The numeric metric ID # path: The fully qualified path to the metric object in Rust # # This map is only filled for metrics, not for pings. # # Example: # # ("COUNTERS", "CounterMetric") -> [(1, "test_only::clicks"), ...] objs_by_type = {} # Map from a metric ID to the fully qualified path of the event object in Rust. # Required for the special handling of event lookups. # # Example: # # 17 -> "test_only::an_event" events_by_id = {} if len(objs) == 1 and "pings" in objs: template_filename = "rust_pings.jinja2" else: template_filename = "rust.jinja2" for category_name, metrics in objs.items(): for metric in metrics.values(): # The constant is all uppercase and suffixed by `_MAP` const_name = util.snake_case(metric.type).upper() + "_MAP" typ = type_name(metric) key = (const_name, typ) metric_name = util.snake_case(metric.name) category_name = util.snake_case(category_name) full_path = f"{category_name}::{metric_name}" if metric.type == "event": events_by_id[get_metric_id(metric)] = full_path continue if key not in objs_by_type: objs_by_type[key] = [] objs_by_type[key].append((get_metric_id(metric), full_path)) # Now for the modules for each category. template = util.get_jinja2_template( template_filename, filters=( ("rust", rust_datatypes_filter), ("snake_case", util.snake_case), ("type_name", type_name), ("extra_type_name", extra_type_name), ("ctor", ctor), ("extra_keys", extra_keys), ("metric_id", get_metric_id), ("ping_id", get_ping_id), ), ) output_fd.write( template.render( all_objs=objs, common_metric_data_args=common_metric_data_args, metric_by_type=objs_by_type, extra_args=util.extra_args, events_by_id=events_by_id, min_submetric_id=2**27 + 1, # One more than 2**ID_BITS from js.py )) output_fd.write("\n")