/** * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ import { runInAction } from "mobx"; import { inspect } from "util"; import { isDefined } from "./type-narrowing"; /** * Get the value behind `key`. If it was not present, first insert `value` * @param map The map to interact with * @param key The key to insert into the map with * @param value The value to optional add to the map * @returns The value in the map */ export function getOrInsert(map: Map, key: K, value: V): V { if (!map.has(key)) { map.set(key, value); } return map.get(key)!; } /** * Updates map and returns the value that was just inserted */ export function put(map: Map, key: K, value: V): V { map.set(key, value); return value; } /** * Like `getOrInsert` but specifically for when `V` is `Map` so that * the typings are inferred correctly. */ export function getOrInsertMap(map: Map>, key: K): Map { return getOrInsert(map, key, new Map()); } /** * Like `getOrInsert` but specifically for when `V` is `Set` so that * the typings are inferred. */ export function getOrInsertSet(map: Map>, key: K): Set { return getOrInsert(map, key, new Set()); } /** * A currying version of {@link getOrInsertSet} */ export function getOrInsertSetFor(map: Map>): (key: K) => Set { return (key) => getOrInsertSet(map, key); } /** * Like `getOrInsert` but with delayed creation of the item. Which is useful * if it is very expensive to create the initial value. */ export function getOrInsertWith(map: Map, key: K, builder: () => V): V; export function getOrInsertWith(map: Map | WeakMap, key: K, builder: () => V): V; export function getOrInsertWith(map: Map | WeakMap, key: K, builder: () => V): V { if (!map.has(key)) { map.set(key, builder()); } return map.get(key)!; } /** * Like {@link getOrInsertWith} but the builder is async and will be awaited before inserting into the map */ export async function getOrInsertWithAsync(map: Map, key: K, asyncBuilder: () => Promise): Promise { if (!map.has(key)) { const newValue = await asyncBuilder(); runInAction(() => { map.set(key, newValue); }); } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return map.get(key)!; } /** * Insert `val` into `map` under `key` and then get the value back */ export function setAndGet(map: Map, key: K, val: V): V { map.set(key, val); return map.get(key)!; } /** * Set the value associated with `key` iff there was not a previous value * @param map The map to interact with * @throws if `key` already in map * @returns `this` so that `strictSet` can be chained */ export function strictSet(map: Map, key: K, val: V): typeof map { if (map.has(key)) { throw new TypeError(`Map already contains key: ${inspect(key)}`); } return map.set(key, val); } /** * Get the value associated with `key` * @param map The map to interact with * @throws if `key` did not a value associated with it */ export function strictGet(map: Map, key: K): V { if (!map.has(key)) { throw new TypeError(`Map does not contains key: ${inspect(key)}`); } return map.get(key)!; } /** * If `key` is in `set`, remove it otherwise add it. * @param set The set to manipulate * @param key The key to toggle the "is in"-ness of */ export function toggle(set: Set, key: K): void { runInAction(() => { // Returns true if value was already in Set; otherwise false. if (!set.delete(key)) { set.add(key); } }); } /** * A helper function to also check for defined-ness */ export function includes(src: T[], value: T | null | undefined): boolean { return isDefined(value) && src.includes(value); }