import { Path, PathTo, PathValue, normalizePath } from "./deep_common"; export const deep_get = >(o: T, p: L, defaultValue: PathValue = undefined): PathValue => { const npath = normalizePath(p); let obj: any = o; for (let key of npath) { if (obj == null) return defaultValue; // if (obj == null || (isObservable(obj) && !mobxHas(obj, key)) || !(key in obj)) return defaultValue; if (obj instanceof Map && obj.has(key)) { obj = obj.get(key); } else if (key in obj) { obj = obj[key]; } else { return defaultValue; } } return obj; } export const deep_operate = , X>(object: T, path: L, operation: (parent_object, key) => X ) => { const npath = normalizePath(path); const parent = deep_get(object, npath.slice(0, -1)); const key = npath[npath.length - 1]; return operation(parent, key); } export const deep_set = >(o: T, p: L, v: PathValue) => { deep_operate(o, p, (obj, key) => { if (obj instanceof Map) { obj.set(key, v); } else { obj[key] = v; } }); } export const deep_delete = >(o: T, p: L) => { deep_operate(o, p, (obj, key) => { if (obj instanceof Map) { obj.delete(key); } else { delete obj[key]; } }); } export function deep_sync(target: any, obj: any, exclude: string[] = []) { const exclusions = exclude.map((exc) => exc.split('.')); const shouldExclude = (key: string) => !!exclusions.find((exc) => exc.length == 1 && exc[0] == key) const subExclusions = (key: string) => exclusions.filter((exc) => exc[0] == key).map((exc) => exc.slice(1).join('.')); for (let [k, v] of Object.entries(obj)) { if (shouldExclude(k)) continue; if (typeof v != typeof target[k] || typeof v != 'object') target[k] = v; else if (Array.isArray(v) && Array.isArray(target[k])) { deep_sync(target[k], v, subExclusions(k)); } else if (v == null || target[k] == null) target[k] = v; else if ((Object.getPrototypeOf(v) != Object.prototype) || (Object.getPrototypeOf(target[k]) != Object.prototype)) target[k] = v; else { deep_sync(target[k], v, subExclusions(k)); } } const keys = Object.keys(target); for (let k of keys) { if (shouldExclude(k as string)) continue; if (!(k in obj)) { if (Array.isArray(target)) target.splice(k as any, 1); else delete target[k] } } return target; } export const SymbolDeepCopy = Symbol("deep_copy"); export function deep_copy(obj: T, handle_instance?: (original: T2, path: any[]) => T2): T { let duplicate; if (typeof obj != 'object' || obj == null) { duplicate = obj; } else if (obj[SymbolDeepCopy]) { duplicate = obj[SymbolDeepCopy](); } else if (Array.isArray(obj)) { duplicate = obj.map((v, i) => deep_copy(v, (original, path) => handle_instance(original, [i, ...path]))); } else if (Object.getPrototypeOf(obj) != Object.prototype) { duplicate = handle_instance(obj, []); } else { duplicate = {} for (let [k, v] of Object.entries(obj)) { duplicate[k] = deep_copy(v, (original, path) => handle_instance(original, [k, ...path])); } } return duplicate; } export function deep_merge(target: T, obj: T) { for (let [k, v] of Object.entries(obj)) { if (typeof v != typeof target[k] || typeof v != 'object') target[k] = v; else if (Array.isArray(v) && Array.isArray(target[k])) { deep_merge(target[k], v); } else if (v == null || target[k] == null) target[k] = v; else if ((Object.getPrototypeOf(v) != Object.prototype) || (Object.getPrototypeOf(target[k]) != Object.prototype)) target[k] = v; else { deep_merge(target[k], v); } } return target; } export const deep_call = any), L extends PathTo>(o: T, p: L, params: Parameters, defaultValue: ReturnType = null): ReturnType => { return deep_operate(o, p, (ctx, key) => { const f = ctx?.[key] as F; if (!f) return defaultValue; return f.apply(ctx, params); }); }