blob: 55d1c125affb1de76764f285346aec89441571bb [file] [log] [blame]
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import * as protobuf from '../gen/protobufjs-light/protobuf';
import {perfetto} from '../gen/protos';
import {
DescriptorProto,
FieldDescriptorProto,
} from './protos';
// Converts FieldDescriptorProto.Type enum to protobufjs friendly strings.
function pbjsFieldType(
type: FieldDescriptorProto.Type|null|undefined,
typeName: string|null|undefined): string {
if (!type) {
if (!typeName) throw Error('type and type_name cannot both be empty.');
return typeName;
}
switch (type) {
case FieldDescriptorProto.Type.TYPE_DOUBLE:
return 'double';
case FieldDescriptorProto.Type.TYPE_FLOAT:
return 'float';
case FieldDescriptorProto.Type.TYPE_INT64:
return 'int64';
case FieldDescriptorProto.Type.TYPE_UINT64:
return 'uint64';
case FieldDescriptorProto.Type.TYPE_INT32:
return 'int32';
case FieldDescriptorProto.Type.TYPE_FIXED64:
return 'fixed64';
case FieldDescriptorProto.Type.TYPE_FIXED32:
return 'fixed32';
case FieldDescriptorProto.Type.TYPE_BOOL:
return 'bool';
case FieldDescriptorProto.Type.TYPE_STRING:
return 'string';
case FieldDescriptorProto.Type.TYPE_GROUP:
throw Error('Field type not supported');
case FieldDescriptorProto.Type.TYPE_MESSAGE:
if (!typeName) throw Error('type_name must be set for TYPE_MESSAGE');
return typeName;
case FieldDescriptorProto.Type.TYPE_BYTES:
return 'bytes';
case FieldDescriptorProto.Type.TYPE_UINT32:
return 'uint32';
case FieldDescriptorProto.Type.TYPE_ENUM:
if (!typeName) throw Error('type_name must be set for TYPE_ENUM');
return typeName;
case FieldDescriptorProto.Type.TYPE_SFIXED32:
return 'sfixed32';
case FieldDescriptorProto.Type.TYPE_SFIXED64:
return 'sfixed64';
case FieldDescriptorProto.Type.TYPE_SINT32:
return 'sint32';
case FieldDescriptorProto.Type.TYPE_SINT64:
return 'sint64';
default:
throw Error('Unexpected FieldDescriptorProto.Type');
}
}
// Converts FieldDescriptorProto.Label enum to protobufjs friendly strings.
function pbjsRuleType(label: FieldDescriptorProto.Label): string {
switch (label) {
case FieldDescriptorProto.Label.LABEL_OPTIONAL:
return 'optional';
case FieldDescriptorProto.Label.LABEL_REQUIRED:
return 'required';
case FieldDescriptorProto.Label.LABEL_REPEATED:
return 'repeated';
default:
throw Error('Unexpected FieldDescriptorProto.Label');
}
}
// In these "valid" interfaces, the members are no longer optional.
interface ValidDescriptorProto extends DescriptorProto {
name: string;
}
type IDescriptorProto = perfetto.protos.IDescriptorProto;
type IFieldDescriptorProto = perfetto.protos.IFieldDescriptorProto;
interface ValidFieldDescriptorProto extends FieldDescriptorProto {
name: string;
number: number;
label: perfetto.protos.FieldDescriptorProto.Label;
}
function assertValidDescriptor(descriptor: IDescriptorProto):
asserts descriptor is ValidDescriptorProto {
if (!descriptor.name) throw Error('Received descriptor with empty name');
}
function assertValidFieldDescriptor(fieldDesc: IFieldDescriptorProto):
asserts fieldDesc is ValidFieldDescriptorProto {
if (!fieldDesc.name) throw Error('Received field with empty name');
if (!fieldDesc.number) throw Error('Received field with empty number');
if (!fieldDesc.label) throw Error('Received field with empty label');
}
export function buildMetricProtoNamespace(descriptors: IDescriptorProto[]):
protobuf.Namespace {
const root = new protobuf.Root();
for (const descriptor of descriptors) {
assertValidDescriptor(descriptor);
const name = descriptor.name;
if (name[0] !== '.') throw Error('Descriptor name must start with "."');
const pieces = name.substring(1).split('.');
if (pieces.length === 0) throw Error('Invalid descriptor name');
// If the name is ".perfetto.protos.MyMetric", we first create the
// namespaces 'perfetto' > 'proto', and then create the type 'MyMetric'
// in the leaf namespace.
const namespace = root.define(pieces.slice(0, -1));
const descType = new protobuf.Type(pieces[pieces.length - 1]);
namespace.add(descType);
for (const field of descriptor.field) {
assertValidFieldDescriptor(field);
const fieldType = pbjsFieldType(field.type, field.typeName);
const rule = pbjsRuleType(field.label);
descType.add(
new protobuf.Field(field.name, field.number, fieldType, rule));
}
}
return root;
}