tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

data_tables.ts (4873B)


      1 import { ResolveType, ZipKeysWithValues } from './types.js';
      2 
      3 export type valueof<K> = K[keyof K];
      4 
      5 export function keysOf<T extends string>(obj: { [k in T]?: unknown }): readonly T[] {
      6  return Object.keys(obj) as unknown[] as T[];
      7 }
      8 
      9 export function numericKeysOf<T extends number>(obj: { [k in T]?: unknown }): readonly T[] {
     10  return Object.keys(obj).map(n => Number(n) as T);
     11 }
     12 
     13 /**
     14 * @returns a new Record from `objects`, using the string returned by Object.toString() as the keys
     15 * and the objects as the values.
     16 */
     17 export function objectsToRecord<T extends Object>(objects: readonly T[]): Record<string, T> {
     18  const record = {};
     19  return objects.reduce((obj, type) => {
     20    return {
     21      ...obj,
     22      [type.toString()]: type,
     23    };
     24  }, record);
     25 }
     26 
     27 /**
     28 * Creates an info lookup object from a more nicely-formatted table. See below for examples.
     29 *
     30 * Note: Using `as const` on the arguments to this function is necessary to infer the correct type.
     31 */
     32 export function makeTable<
     33  Members extends readonly string[],
     34  Defaults extends readonly unknown[],
     35  Table extends { readonly [k: string]: readonly unknown[] },
     36 >(
     37  members: Members,
     38  defaults: Defaults,
     39  table: Table
     40 ): {
     41  readonly [k in keyof Table]: ResolveType<ZipKeysWithValues<Members, Table[k], Defaults>>;
     42 } {
     43  const result: { [k: string]: { [m: string]: unknown } } = {};
     44  for (const [k, v] of Object.entries<readonly unknown[]>(table)) {
     45    const item: { [m: string]: unknown } = {};
     46    for (let i = 0; i < members.length; ++i) {
     47      item[members[i]] = v[i] ?? defaults[i];
     48    }
     49    result[k] = item;
     50  }
     51  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
     52  return result as any;
     53 }
     54 
     55 /**
     56 * Creates an info lookup object from a more nicely-formatted table.
     57 *
     58 * Note: Using `as const` on the arguments to this function is necessary to infer the correct type.
     59 *
     60 * Example:
     61 *
     62 * ```
     63 * const t = makeTableWithDefaults(
     64 *   { c: 'default' },       // columnRenames
     65 *   ['a', 'default', 'd'],  // columnsKept
     66 *   ['a', 'b', 'c', 'd'],   // columns
     67 *   [123, 456, 789, 1011],  // defaults
     68 *   {                       // table
     69 *     foo: [1, 2, 3, 4],
     70 *     bar: [5,  ,  , 8],
     71 *     moo: [ , 9,10,  ],
     72 *   }
     73 * );
     74 *
     75 * // t = {
     76 * //   foo: { a:   1, default:   3, d:    4 },
     77 * //   bar: { a:   5, default: 789, d:    8 },
     78 * //   moo: { a: 123, default:  10, d: 1011 },
     79 * // };
     80 * ```
     81 *
     82 * MAINTENANCE_TODO: `ZipKeysWithValues<Members, Table[k], Defaults>` is incorrect
     83 * because Members no longer maps to Table[k]. It's not clear if this is even possible to fix
     84 * because it requires mapping, not zipping. Maybe passing in a index mapping
     85 * would fix it (which is gross) but if you have columnsKept as [0, 2, 3] then maybe it would
     86 * be possible to generate the correct type? I don't think we can generate the map at compile time
     87 * so we'd have to hand code it. Other ideas, don't generate kLimitsInfoCore and kLimitsInfoCompat
     88 * where they are keys of infos. Instead, generate kLimitsInfoCoreDefaults, kLimitsInfoCoreMaximums,
     89 * kLimitsInfoCoreClasses where each is just a `{[k: string]: type}`. Could zip those after or,
     90 * maybe that suggests passing in the hard coded indices would work.
     91 *
     92 * @param columnRenames the name of the column in the table that will be assigned to the 'default' property of each entry.
     93 * @param columnsKept the names of properties you want in the generated lookup table. This must be a subset of the columns of the tables except for the name 'default' which is looked from the previous argument.
     94 * @param columns the names of the columns of the name
     95 * @param defaults the default value by column for any element in a row of the table that is undefined
     96 * @param table named table rows.
     97 */
     98 export function makeTableRenameAndFilter<
     99  Members extends readonly string[],
    100  DataMembers extends readonly string[],
    101  Defaults extends readonly unknown[],
    102  Table extends { readonly [k: string]: readonly unknown[] },
    103 >(
    104  columnRenames: { [key: string]: string },
    105  columnsKept: Members,
    106  columns: DataMembers,
    107  defaults: Defaults,
    108  table: Table
    109 ): {
    110  readonly [k in keyof Table]: ResolveType<ZipKeysWithValues<Members, Table[k], Defaults>>;
    111 } {
    112  const result: { [k: string]: { [m: string]: unknown } } = {};
    113  const keyToIndex = new Map<string, number>(
    114    columnsKept.map(name => {
    115      const remappedName = columnRenames[name] === undefined ? name : columnRenames[name];
    116      return [name, columns.indexOf(remappedName)];
    117    })
    118  );
    119  for (const [k, v] of Object.entries<readonly unknown[]>(table)) {
    120    const item: { [m: string]: unknown } = {};
    121    for (const member of columnsKept) {
    122      const ndx = keyToIndex.get(member)!;
    123      item[member] = v[ndx] ?? defaults[ndx];
    124    }
    125    result[k] = item;
    126  }
    127  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
    128  return result as any;
    129 }