ui: Stdlib support prototype

Plumbing of stdlib into the UI. Parses the stdlib json and deserialises it into typescript objects. This is a noop.

This is the first step into being able to open any stdlib table in the UI, viewing the list of packages/modules/tables and seeing the column description.

Change-Id: I52a8b3b4824ecf0b5392216aff8ef34d482ce209
diff --git a/ui/src/core/default_plugins.ts b/ui/src/core/default_plugins.ts
index 4791e8e..caba9f0 100644
--- a/ui/src/core/default_plugins.ts
+++ b/ui/src/core/default_plugins.ts
@@ -60,6 +60,7 @@
   'dev.perfetto.RestorePinnedTrack',
   'dev.perfetto.Sched',
   'dev.perfetto.Screenshots',
+  'dev.perfetto.SqlModules',
   'dev.perfetto.Thread',
   'dev.perfetto.ThreadState',
   'dev.perfetto.TimelineSync',
diff --git a/ui/src/plugins/dev.perfetto.SqlModules/index.ts b/ui/src/plugins/dev.perfetto.SqlModules/index.ts
new file mode 100644
index 0000000..f2fde8d
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.SqlModules/index.ts
@@ -0,0 +1,35 @@
+// Copyright (C) 2024 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 {assetSrc} from '../../base/assets';
+import {assertExists} from '../../base/logging';
+import {PerfettoPlugin} from '../../public/plugin';
+import {SqlModules} from './sql_modules';
+import {SQL_MODULES_DOCS_SCHEMA, SqlModulesImpl} from './sql_modules_impl';
+
+export default class implements PerfettoPlugin {
+  static readonly id = 'dev.perfetto.SqlModules';
+  private sqlModules?: SqlModules;
+
+  async onTraceLoad() {
+    const resp = await fetch(assetSrc('stdlib_docs.json'));
+    const json = await resp.json();
+    const docs = SQL_MODULES_DOCS_SCHEMA.parse(json);
+    this.sqlModules = new SqlModulesImpl(docs);
+  }
+
+  getSqlModules() {
+    return assertExists(this.sqlModules);
+  }
+}
diff --git a/ui/src/plugins/dev.perfetto.SqlModules/sql_modules.ts b/ui/src/plugins/dev.perfetto.SqlModules/sql_modules.ts
new file mode 100644
index 0000000..9f0503a
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.SqlModules/sql_modules.ts
@@ -0,0 +1,90 @@
+// Copyright (C) 2024 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.
+
+// Handles the access to all of the Perfetto SQL modules accessible to Trace
+//  Processor.
+export interface SqlModules {
+  // Returns names of all tables/views between all loaded Perfetto SQL modules.
+  listTables(): string[];
+
+  // Returns Perfetto SQL table/view if it was loaded in one of the Perfetto
+  // SQL module.
+  getTable(tableName: string): SqlTable | undefined;
+}
+
+// Handles the access to a specific Perfetto SQL Package. Package consists of
+// Perfetto SQL modules.
+export interface SqlPackage {
+  readonly name: string;
+  readonly modules: SqlModule[];
+  listTables(): string[];
+  getTable(tableName: string): SqlTable | undefined;
+}
+
+// Handles the access to a specific Perfetto SQL module.
+export interface SqlModule {
+  readonly includeKey: string;
+  readonly dataObjects: SqlTable[];
+  readonly functions: SqlFunction[];
+  readonly tableFunctions: SqlTableFunction[];
+  readonly macros: SqlMacro[];
+}
+
+// The definition of Perfetto SQL table/view.
+export interface SqlTable {
+  readonly name: string;
+  readonly description: string;
+  readonly type: string;
+  readonly columns: SqlColumn[];
+}
+
+// The definition of Perfetto SQL function.
+export interface SqlFunction {
+  readonly name: string;
+  readonly description: string;
+  readonly args: SqlArgument[];
+  readonly returnType: string;
+  readonly returnDesc: string;
+}
+
+// The definition of Perfetto SQL table function.
+export interface SqlTableFunction {
+  readonly name: string;
+  readonly description: string;
+  readonly args: SqlArgument[];
+  readonly returnCols: SqlColumn[];
+}
+
+// The definition of Perfetto SQL macro.
+export interface SqlMacro {
+  readonly name: string;
+  readonly description: string;
+  readonly args: SqlArgument[];
+  readonly returnType: string;
+}
+
+// The definition of Perfetto SQL column.
+export interface SqlColumn {
+  readonly name: string;
+  readonly description: string;
+  readonly type: string;
+}
+
+// The definition of Perfetto SQL argument. Can be used for functions, table
+// functions or macros.
+export interface SqlArgument {
+  readonly name: string;
+  readonly description: string;
+  readonly type: string;
+}
diff --git a/ui/src/plugins/dev.perfetto.SqlModules/sql_modules_impl.ts b/ui/src/plugins/dev.perfetto.SqlModules/sql_modules_impl.ts
new file mode 100644
index 0000000..ee6fe79
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.SqlModules/sql_modules_impl.ts
@@ -0,0 +1,254 @@
+// Copyright (C) 2024 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 {z} from 'zod';
+import {
+  SqlModules,
+  SqlColumn,
+  SqlFunction,
+  SqlArgument,
+  SqlMacro,
+  SqlModule,
+  SqlPackage,
+  SqlTable,
+  SqlTableFunction,
+} from './sql_modules';
+
+export class SqlModulesImpl implements SqlModules {
+  readonly packages: SqlPackage[];
+
+  constructor(docs: SqlModulesDocsSchema) {
+    this.packages = docs.map((json) => new StdlibPackageImpl(json));
+  }
+
+  listTables(): string[] {
+    const tables: string[] = [];
+    for (const stdlibPackage of this.packages) {
+      tables.concat(stdlibPackage.listTables());
+    }
+    return tables;
+  }
+
+  getTable(tableName: string): SqlTable | undefined {
+    for (const stdlibPackage of this.packages) {
+      const maybeTable = stdlibPackage.getTable(tableName);
+      if (maybeTable) {
+        return maybeTable;
+      }
+    }
+    return undefined;
+  }
+}
+
+export class StdlibPackageImpl implements SqlPackage {
+  readonly name: string;
+  readonly modules: SqlModule[];
+
+  constructor(docs: DocsPackageSchemaType) {
+    this.name = docs.name;
+    this.modules = [];
+    for (const moduleJson of docs.modules) {
+      this.modules.push(new StdlibModuleImpl(moduleJson));
+    }
+  }
+
+  getTable(tableName: string): SqlTable | undefined {
+    for (const module of this.modules) {
+      for (const dataObj of module.dataObjects) {
+        if (dataObj.name == tableName) {
+          return dataObj;
+        }
+      }
+    }
+    return undefined;
+  }
+
+  listTables(): string[] {
+    return this.modules.flatMap((module) =>
+      module.dataObjects.map((dataObj) => dataObj.name),
+    );
+  }
+}
+
+export class StdlibModuleImpl implements SqlModule {
+  readonly includeKey: string;
+  readonly dataObjects: SqlTable[];
+  readonly functions: SqlFunction[];
+  readonly tableFunctions: SqlTableFunction[];
+  readonly macros: SqlMacro[];
+
+  constructor(docs: DocsModuleSchemaType) {
+    this.includeKey = docs.module_name;
+    this.dataObjects = docs.data_objects.map(
+      (json) => new StdlibDataObjectImpl(json),
+    );
+    this.functions = docs.functions.map((json) => new StdlibFunctionImpl(json));
+    this.tableFunctions = docs.table_functions.map(
+      (json) => new StdlibTableFunctionImpl(json),
+    );
+    this.macros = docs.macros.map((json) => new StdlibMacroImpl(json));
+  }
+}
+
+class StdlibMacroImpl implements SqlMacro {
+  readonly name: string;
+  readonly summaryDesc: string;
+  readonly description: string;
+  readonly args: SqlArgument[];
+  readonly returnType: string;
+
+  constructor(docs: DocsMacroSchemaType) {
+    this.name = docs.name;
+    this.summaryDesc = docs.summary_desc;
+    this.description = docs.desc;
+    this.returnType = docs.return_type;
+    this.args = [];
+    this.args = docs.args.map((json) => new StdlibFunctionArgImpl(json));
+  }
+}
+
+class StdlibTableFunctionImpl implements SqlTableFunction {
+  readonly name: string;
+  readonly summaryDesc: string;
+  readonly description: string;
+  readonly args: SqlArgument[];
+  readonly returnCols: SqlColumn[];
+
+  constructor(docs: DocsTableFunctionSchemaType) {
+    this.name = docs.name;
+    this.summaryDesc = docs.summary_desc;
+    this.description = docs.desc;
+    this.args = docs.args.map((json) => new StdlibFunctionArgImpl(json));
+    this.returnCols = docs.cols.map((json) => new StdlibColumnImpl(json));
+  }
+}
+
+class StdlibFunctionImpl implements SqlFunction {
+  readonly name: string;
+  readonly summaryDesc: string;
+  readonly description: string;
+  readonly args: SqlArgument[];
+  readonly returnType: string;
+  readonly returnDesc: string;
+
+  constructor(docs: DocsFunctionSchemaType) {
+    this.name = docs.name;
+    this.summaryDesc = docs.summary_desc;
+    this.description = docs.desc;
+    this.returnType = docs.return_type;
+    this.returnDesc = docs.return_desc;
+    this.args = docs.args.map((json) => new StdlibFunctionArgImpl(json));
+  }
+}
+
+class StdlibDataObjectImpl implements SqlTable {
+  name: string;
+  description: string;
+  type: string;
+  columns: SqlColumn[];
+
+  constructor(docs: DocsDataObjectSchemaType) {
+    this.name = docs.name;
+    this.description = docs.desc;
+    this.type = docs.type;
+    this.columns = docs.cols.map((json) => new StdlibColumnImpl(json));
+  }
+}
+
+class StdlibColumnImpl implements SqlColumn {
+  name: string;
+  type: string;
+  description: string;
+
+  constructor(docs: DocsArgOrColSchemaType) {
+    this.type = docs.type;
+    this.description = docs.desc;
+    this.name = docs.name;
+  }
+}
+
+class StdlibFunctionArgImpl implements SqlArgument {
+  name: string;
+  description: string;
+  type: string;
+
+  constructor(docs: DocsArgOrColSchemaType) {
+    this.type = docs.type;
+    this.description = docs.desc;
+    this.name = docs.name;
+  }
+}
+
+const ARG_OR_COL_SCHEMA = z.object({
+  name: z.string(),
+  type: z.string(),
+  desc: z.string(),
+});
+type DocsArgOrColSchemaType = z.infer<typeof ARG_OR_COL_SCHEMA>;
+
+const DATA_OBJECT_SCHEMA = z.object({
+  name: z.string(),
+  desc: z.string(),
+  summary_desc: z.string(),
+  type: z.string(),
+  cols: z.array(ARG_OR_COL_SCHEMA),
+});
+type DocsDataObjectSchemaType = z.infer<typeof DATA_OBJECT_SCHEMA>;
+
+const FUNCTION_SCHEMA = z.object({
+  name: z.string(),
+  desc: z.string(),
+  summary_desc: z.string(),
+  args: z.array(ARG_OR_COL_SCHEMA),
+  return_type: z.string(),
+  return_desc: z.string(),
+});
+type DocsFunctionSchemaType = z.infer<typeof FUNCTION_SCHEMA>;
+
+const TABLE_FUNCTION_SCHEMA = z.object({
+  name: z.string(),
+  desc: z.string(),
+  summary_desc: z.string(),
+  args: z.array(ARG_OR_COL_SCHEMA),
+  cols: z.array(ARG_OR_COL_SCHEMA),
+});
+type DocsTableFunctionSchemaType = z.infer<typeof TABLE_FUNCTION_SCHEMA>;
+
+const MACRO_SCHEMA = z.object({
+  name: z.string(),
+  desc: z.string(),
+  summary_desc: z.string(),
+  return_desc: z.string(),
+  return_type: z.string(),
+  args: z.array(ARG_OR_COL_SCHEMA),
+});
+type DocsMacroSchemaType = z.infer<typeof MACRO_SCHEMA>;
+
+const MODULE_SCHEMA = z.object({
+  module_name: z.string(),
+  data_objects: z.array(DATA_OBJECT_SCHEMA),
+  functions: z.array(FUNCTION_SCHEMA),
+  table_functions: z.array(TABLE_FUNCTION_SCHEMA),
+  macros: z.array(MACRO_SCHEMA),
+});
+type DocsModuleSchemaType = z.infer<typeof MODULE_SCHEMA>;
+
+const PACKAGE_SCHEMA = z.object({
+  name: z.string(),
+  modules: z.array(MODULE_SCHEMA),
+});
+type DocsPackageSchemaType = z.infer<typeof PACKAGE_SCHEMA>;
+
+export const SQL_MODULES_DOCS_SCHEMA = z.array(PACKAGE_SCHEMA);
+export type SqlModulesDocsSchema = z.infer<typeof SQL_MODULES_DOCS_SCHEMA>;
diff --git a/ui/src/public/lib/stdlib_docs.ts b/ui/src/public/lib/stdlib_docs.ts
deleted file mode 100644
index fcdcf18..0000000
--- a/ui/src/public/lib/stdlib_docs.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2024 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 {assetSrc} from '../../base/assets';
-
-// Fetch the stdlib docs
-export async function getStdlibDocs(): Promise<string> {
-  const resp = await fetch(assetSrc('stdlib_docs.json'));
-  const json = await resp.json();
-  return JSON.parse(json);
-}