69 lines
1.4 KiB
TypeScript
69 lines
1.4 KiB
TypeScript
|
// 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<ObjectMappingSpecType>
|
||
|
| Readonly<{
|
||
|
key: string;
|
||
|
valueSpec: ObjectMappingSpecType;
|
||
|
}>
|
||
|
| Readonly<{
|
||
|
isMap: true;
|
||
|
valueSpec: ObjectMappingSpecType;
|
||
|
}>;
|
||
|
|
||
|
export function mapObjectWithSpec<Input, Output>(
|
||
|
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;
|
||
|
}
|