1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2023 The Android Open Source Project 2*6dbdd20aSAndroid Build Coastguard Worker// 3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*6dbdd20aSAndroid Build Coastguard Worker// 7*6dbdd20aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*6dbdd20aSAndroid Build Coastguard Worker// 9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License. 14*6dbdd20aSAndroid Build Coastguard Worker 15*6dbdd20aSAndroid Build Coastguard Workerimport {assertExists} from './logging'; 16*6dbdd20aSAndroid Build Coastguard Workerimport {exists} from './utils'; 17*6dbdd20aSAndroid Build Coastguard Worker 18*6dbdd20aSAndroid Build Coastguard Workerexport type PathKey = string | number; 19*6dbdd20aSAndroid Build Coastguard Workerexport type Path = PathKey[]; 20*6dbdd20aSAndroid Build Coastguard Worker 21*6dbdd20aSAndroid Build Coastguard Worker/** 22*6dbdd20aSAndroid Build Coastguard Worker * Gets the |value| at a |path| of |object|. If a portion of the path doesn't 23*6dbdd20aSAndroid Build Coastguard Worker * exist, |undefined| is returned. 24*6dbdd20aSAndroid Build Coastguard Worker * 25*6dbdd20aSAndroid Build Coastguard Worker * Example: 26*6dbdd20aSAndroid Build Coastguard Worker * const obj = { 27*6dbdd20aSAndroid Build Coastguard Worker * a: [ 28*6dbdd20aSAndroid Build Coastguard Worker * {b: 'c'}, 29*6dbdd20aSAndroid Build Coastguard Worker * {d: 'e', f: 123}, 30*6dbdd20aSAndroid Build Coastguard Worker * ], 31*6dbdd20aSAndroid Build Coastguard Worker * }; 32*6dbdd20aSAndroid Build Coastguard Worker * getPath(obj, ['a']) -> [{b: 'c'}, {d: 'e', f: 123}] 33*6dbdd20aSAndroid Build Coastguard Worker * getPath(obj, ['a', 1]) -> {d: 'e', f: 123} 34*6dbdd20aSAndroid Build Coastguard Worker * getPath(obj, ['a', 1, 'd']) -> 'e' 35*6dbdd20aSAndroid Build Coastguard Worker * getPath(obj, ['g']) -> undefined 36*6dbdd20aSAndroid Build Coastguard Worker * getPath(obj, ['g', 'h']) -> undefined 37*6dbdd20aSAndroid Build Coastguard Worker * 38*6dbdd20aSAndroid Build Coastguard Worker * Note: This is an appropriate use of `any`, as we are knowingly getting fast 39*6dbdd20aSAndroid Build Coastguard Worker * and loose with the type system in this function: it's basically JavaScript. 40*6dbdd20aSAndroid Build Coastguard Worker * Attempting to pretend it's anything else would result in superfluous type 41*6dbdd20aSAndroid Build Coastguard Worker * assertions which would serve no benefit. 42*6dbdd20aSAndroid Build Coastguard Worker */ 43*6dbdd20aSAndroid Build Coastguard Worker// eslint-disable-next-line @typescript-eslint/no-explicit-any 44*6dbdd20aSAndroid Build Coastguard Workerexport function getPath<T>(obj: any, path: Path): T | undefined { 45*6dbdd20aSAndroid Build Coastguard Worker let x = obj; 46*6dbdd20aSAndroid Build Coastguard Worker for (const node of path) { 47*6dbdd20aSAndroid Build Coastguard Worker if (x === undefined) return undefined; 48*6dbdd20aSAndroid Build Coastguard Worker x = x[node]; 49*6dbdd20aSAndroid Build Coastguard Worker } 50*6dbdd20aSAndroid Build Coastguard Worker return x; 51*6dbdd20aSAndroid Build Coastguard Worker} 52*6dbdd20aSAndroid Build Coastguard Worker 53*6dbdd20aSAndroid Build Coastguard Worker/** 54*6dbdd20aSAndroid Build Coastguard Worker * Sets the |value| at |path| of |object|. If the final node of the path doesn't 55*6dbdd20aSAndroid Build Coastguard Worker * exist, the value will be created. Otherwise, TypeError is thrown. 56*6dbdd20aSAndroid Build Coastguard Worker * 57*6dbdd20aSAndroid Build Coastguard Worker * Example: 58*6dbdd20aSAndroid Build Coastguard Worker * const obj = { 59*6dbdd20aSAndroid Build Coastguard Worker * a: [ 60*6dbdd20aSAndroid Build Coastguard Worker * {b: 'c'}, 61*6dbdd20aSAndroid Build Coastguard Worker * {d: 'e', f: 123}, 62*6dbdd20aSAndroid Build Coastguard Worker * ], 63*6dbdd20aSAndroid Build Coastguard Worker * }; 64*6dbdd20aSAndroid Build Coastguard Worker * setPath(obj, ['a'], 'foo') -> {a: 'foo'} 65*6dbdd20aSAndroid Build Coastguard Worker * setPath(obj, ['a', 1], 'foo') -> {a: [{b: 'c'}, 'foo']} 66*6dbdd20aSAndroid Build Coastguard Worker * setPath(obj, ['g'], 'foo') -> {a: [...], g: 'foo'} 67*6dbdd20aSAndroid Build Coastguard Worker * setPath(obj, ['g', 'h'], 'foo') -> TypeError! 68*6dbdd20aSAndroid Build Coastguard Worker */ 69*6dbdd20aSAndroid Build Coastguard Worker// eslint-disable-next-line @typescript-eslint/no-explicit-any 70*6dbdd20aSAndroid Build Coastguard Workerexport function setPath<T>(obj: any, path: Path, value: T): void { 71*6dbdd20aSAndroid Build Coastguard Worker const pathClone = [...path]; 72*6dbdd20aSAndroid Build Coastguard Worker let o = obj; 73*6dbdd20aSAndroid Build Coastguard Worker while (pathClone.length > 1) { 74*6dbdd20aSAndroid Build Coastguard Worker const p = assertExists(pathClone.shift()); 75*6dbdd20aSAndroid Build Coastguard Worker o = o[p]; 76*6dbdd20aSAndroid Build Coastguard Worker } 77*6dbdd20aSAndroid Build Coastguard Worker 78*6dbdd20aSAndroid Build Coastguard Worker const p = pathClone.shift(); 79*6dbdd20aSAndroid Build Coastguard Worker if (!exists(p)) { 80*6dbdd20aSAndroid Build Coastguard Worker throw TypeError('Path array is empty'); 81*6dbdd20aSAndroid Build Coastguard Worker } 82*6dbdd20aSAndroid Build Coastguard Worker o[p] = value; 83*6dbdd20aSAndroid Build Coastguard Worker} 84*6dbdd20aSAndroid Build Coastguard Worker 85*6dbdd20aSAndroid Build Coastguard Workerexport function shallowEquals(a: unknown, b: unknown) { 86*6dbdd20aSAndroid Build Coastguard Worker if (a === b) { 87*6dbdd20aSAndroid Build Coastguard Worker return true; 88*6dbdd20aSAndroid Build Coastguard Worker } 89*6dbdd20aSAndroid Build Coastguard Worker if (a === undefined || b === undefined) { 90*6dbdd20aSAndroid Build Coastguard Worker return false; 91*6dbdd20aSAndroid Build Coastguard Worker } 92*6dbdd20aSAndroid Build Coastguard Worker if (a === null || b === null) { 93*6dbdd20aSAndroid Build Coastguard Worker return false; 94*6dbdd20aSAndroid Build Coastguard Worker } 95*6dbdd20aSAndroid Build Coastguard Worker const objA = a as {[_: string]: {}}; 96*6dbdd20aSAndroid Build Coastguard Worker const objB = b as {[_: string]: {}}; 97*6dbdd20aSAndroid Build Coastguard Worker for (const key of Object.keys(objA)) { 98*6dbdd20aSAndroid Build Coastguard Worker if (objA[key] !== objB[key]) { 99*6dbdd20aSAndroid Build Coastguard Worker return false; 100*6dbdd20aSAndroid Build Coastguard Worker } 101*6dbdd20aSAndroid Build Coastguard Worker } 102*6dbdd20aSAndroid Build Coastguard Worker for (const key of Object.keys(objB)) { 103*6dbdd20aSAndroid Build Coastguard Worker if (objA[key] !== objB[key]) { 104*6dbdd20aSAndroid Build Coastguard Worker return false; 105*6dbdd20aSAndroid Build Coastguard Worker } 106*6dbdd20aSAndroid Build Coastguard Worker } 107*6dbdd20aSAndroid Build Coastguard Worker return true; 108*6dbdd20aSAndroid Build Coastguard Worker} 109*6dbdd20aSAndroid Build Coastguard Worker 110*6dbdd20aSAndroid Build Coastguard Workerexport function isString(s: unknown): s is string { 111*6dbdd20aSAndroid Build Coastguard Worker return typeof s === 'string' || s instanceof String; 112*6dbdd20aSAndroid Build Coastguard Worker} 113*6dbdd20aSAndroid Build Coastguard Worker 114*6dbdd20aSAndroid Build Coastguard Worker// Given a string enum |enum|, check that |value| is a valid member of |enum|. 115*6dbdd20aSAndroid Build Coastguard Workerexport function isEnumValue<T extends {}>( 116*6dbdd20aSAndroid Build Coastguard Worker enm: T, 117*6dbdd20aSAndroid Build Coastguard Worker value: unknown, 118*6dbdd20aSAndroid Build Coastguard Worker): value is T[keyof T] { 119*6dbdd20aSAndroid Build Coastguard Worker return Object.values(enm).includes(value); 120*6dbdd20aSAndroid Build Coastguard Worker} 121