// Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable @typescript-eslint/no-explicit-any */ import { cloneDeep, get, set } from 'lodash'; export type ObjectMappingSpecType = | string | ReadonlyArray | Readonly<{ key: string; valueSpec: ObjectMappingSpecType; }> | Readonly<{ isMap: true; valueSpec: ObjectMappingSpecType; }>; export function mapObjectWithSpec( spec: ObjectMappingSpecType, // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types data: any, map: (value: Input) => Output, target = cloneDeep(data) ): any { if (!data) { return target; } if (typeof spec === 'string') { const value = get(data, spec); if (value) { set(target, spec, map(value)); } return target; } if ('isMap' in spec) { for (const key of Object.keys(data)) { // eslint-disable-next-line no-param-reassign target[key] = mapObjectWithSpec( spec.valueSpec, data[key], map, target[key] ); } return target; } if ('key' in spec) { // eslint-disable-next-line no-param-reassign target[spec.key] = mapObjectWithSpec( spec.valueSpec, data[spec.key], map, target[spec.key] ); return target; } for (const key of spec) { mapObjectWithSpec(key, data, map, target); } return target; }