// Copyright (C) 2023 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. // A single value. These are often retived from trace_processor so // need to map to the related sqlite type: // null = NULL, string = TEXT, number = INTEGER/REAL, boolean = INTEGER export type Primitive = null|string|boolean|number; export const NullType = null; export const NumType = 0 as const; export const StrType = 'str' as const; export const IdType = 'id' as const; export const BoolType = true as const; // Values may be of any of the above types: type KeyType = typeof NumType|typeof StrType|typeof NullType|typeof IdType|typeof BoolType; // KeySet is a specification for the key/value pairs on an Event. // - Every event must have a string ID. // - In addition Events may have 1 or more key/value pairs. // The *specification* for the key/value pair has to be *precisely* one // of the KeySet constants above. So: // const thisTypeChecks: KeySet = { id: IdType, foo: StrType }; // const thisDoesNot: KeySet = { id: IdType, foo: "bar" }; // Since although are is a string it's not a KeySet. export type KeySet = { readonly id: typeof IdType, readonly [key: string]: KeyType, }; export interface EmptyKeySet extends KeySet { readonly id: typeof IdType; } // A particular key/value pair on an Event matches the relevant entry // on the KeySet if the KeyType and the value type 'match': // IdType => string // StrType => string // BoolType => boolean // NullType => null // NumType => number type IsExactly = P extends Q ? (Q extends P ? any : never) : never; type IsId = T extends IsExactly? string : never; type IsStr = T extends IsExactly? string : never; type IsNum = T extends IsExactly? number : never; type IsBool = T extends IsExactly? boolean : never; type IsNull = T extends IsExactly? null : never; type MapType = IsId|IsStr|IsNum|IsBool|IsNull; type ConformingValue = T extends MapType? MapType: void; // A single trace Event. // Events have: // - A globally unique identifier `id`. // - Zero or more key/value pairs. // Note: Events do *not* have to have all possible keys/value pairs for // the given id. It is expected that users will only materialise the // key/value pairs relevant to the specific use case at hand. export type UntypedEvent = { readonly id: string, readonly [key: string]: Primitive, }; export type Event = { [Property in keyof K]: ConformingValue; }; type KeyUnion = P&Q; // An EventSet is a: // - ordered // - immutable // - subset // of events in the trace. export interface EventSet

{ // All possible keys for Events in this EventSet. readonly keys: KeySet; // Methods for refining the set. // Note: these are all synchronous - we expect the cost (and hence // any asynchronous queries) to be deferred to analysis time. filter(...filters: Filter[]): EventSet

; sort(...sorts: Sort[]): EventSet

; union(other: EventSet): EventSet>; intersect(other: EventSet): EventSet>; // Methods for analysing the set. // Note: these are all asynchronous - it's expected that these will // often have to do queries. count(): Promise; isEmpty(): Promise; materialise(keys: T, offset?: number, limit?: number): Promise>; } export type UntypedEventSet = EventSet; // An expression that operates on an Event and produces a Primitive as // output. Expressions have to work both in JavaScript and in SQL. // In SQL users can use buildQueryFragment to convert the expression // into a snippet of SQL. For JavaScript they call execute(). In both // cases you need to know which keys the expression uses, for this call // `freeVariables`. export interface Expr { // Return a fragment of SQL that can be used to evaluate the // expression. `binding` maps key names to column names in the // resulting SQL. The caller must ensure that binding includes at // least all the keys from `freeVariables`. buildQueryFragment(binding: Map): string; // Run the expression on an Event. The caller must ensure that event // has all the keys from `freeVariables` materialised. execute(event: UntypedEvent): Primitive; // Returns the set of keys used in this expression. // For example in an expression representing `(foo + 4) * bar` // freeVariables would return the set {'foo', 'bar'}. freeVariables(): Set; } // A filter is a (normally boolean) expression. export type Filter = Expr; // Sorting direction. export enum Direction { ASC, DESC, } // A sort is an expression combined with a direction: export interface Sort { direction: Direction; expression: Expr; } // An EventSet where the Event are accesible synchronously. interface ConcreteEventSet extends EventSet { readonly events: Event[]; } export type UntypedConcreteEventSet = ConcreteEventSet;