# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Abstract class definition of Code Generation from structured.xml
into definitions for different targets."""
from abc import ABC, abstractmethod
from codegen_util import Util
# Default target if not explicitly specified in the XML.
DEFAULT_TARGET = "chromium"
class EventTemplateBase(ABC):
"""EventTemplate is a base abstract class, which contains the common
abstract methods and common constructor to generate code for
structured events."""
def __init__(self, model, dirname, basename, file_template, project_template,
enum_template, event_template, metric_template):
self.model = model
self.dirname = dirname
self.basename = basename
self.file_template = file_template
self.project_template = project_template
self.event_template = event_template
self.enum_template = enum_template
self.metric_template = metric_template
@abstractmethod
def write_file(self):
pass
class ProjectInfoBase(ABC):
"""Codegen-related info about a Structured Metrics project.
Constructor takes in a set of target_names in order to determine
whether a project should have its code generated."""
def __init__(self, project, target_names):
self.name = Util.sanitize_name(project.name)
self.namespace = Util.camel_to_snake(self.name)
self.name_hash = Util.hash_name(self.name)
self.events = project.events
self.enums = project.enums
self.targets = project.targets
self.target_names = target_names
self.platform = project.platform
# Set ID type.
if project.id == 'uma':
self.id_type = 'kUmaId'
elif project.id == 'per-project':
self.id_type = 'kProjectId'
elif project.id == 'none':
self.id_type = 'kUnidentified'
else:
raise ValueError('Invalid id type.')
# Set ID scope
if project.scope == 'profile':
self.id_scope = 'kPerProfile'
elif project.scope == 'device':
self.id_scope = 'kPerDevice'
else:
raise ValueError('Invalid id scope.')
# Set event type. This is inferred by checking all metrics within the
# project. If any of a project's metrics is a raw string, then its events
# are considered raw string events, even if they also contain non-strings.
self.event_type = 'REGULAR'
for event in project.events:
for metric in event.metrics:
if metric.type == 'raw-string':
self.event_type = 'RAW_STRING'
break
# Check if event is part of an event sequence. Note that this goes after the
# raw string check since the type has higher priority.
if project.is_event_sequence_project:
self.is_event_sequence = 'true'
self.event_type = 'SEQUENCE'
else:
self.is_event_sequence = 'false'
self.key_rotation_period = project.key_rotation_period
if len(self.targets) == 0:
self.targets.add(DEFAULT_TARGET)
# Check if the Project should be generated for the given target.
self.should_codegen = False
for target in self.targets:
if target in self.target_names:
self.should_codegen = True
class EventInfoBase(ABC):
"""Codegen-related info about a Structured Metrics event."""
def __init__(self, event, project_info):
self.name = Util.sanitize_name(event.name)
self.project_name = project_info.name
self.platform = project_info.platform
self.name_hash = Util.event_name_hash(project_info.name, self.name,
project_info.platform)
self.is_event_sequence = project_info.is_event_sequence
self.force_record = str(event.force_record).lower()
self.metrics = event.metrics
class MetricInfoBase(ABC):
"""Codegen-related info about a Structured Metrics metric."""
def __init__(self, metric):
self.name = Util.sanitize_name(metric.name)
self.hash = Util.hash_name(metric.name)
self.is_enum = metric.is_enum