-
Alexander Rose authoredAlexander Rose authored
value-cell.ts 2.21 KiB
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { idFactory } from './id-factory'
/** A mutable value reference. */
interface ValueRef<T> { ref: T }
namespace ValueRef {
export function create<T>(ref: T): ValueRef<T> { return { ref }; }
export function set<T>(ref: ValueRef<T>, value: T) { ref.ref = value; return ref; }
}
const getNextId = idFactory(0, 0x7FFFFFFF)
/**
* An immutable value box that also holds a version of the attribute.
* Optionally includes automatically propadated "metadata".
*/
type ValueBox<T, D = never> = {
/** Unique identifier in the range 0 to 0x7FFFFFFF */
readonly id: number,
readonly version: number,
readonly metadata: D,
readonly value: T,
}
namespace ValueBox {
export function create<T, D = never>(value: T, metadata?: D): ValueBox<T, D> {
return { id: getNextId(), version: 0, value, metadata: metadata! };
}
/** The box.metadata is carried over from the old box */
export function withValue<T, D>(box: ValueBox<T, D>, value: T): ValueBox<T, D> {
return { id: box.id, version: box.version + 1, value, metadata: box.metadata };
}
}
/** An immutable box stored inside a mutable cell. */
type ValueCell<T, D = never> = ValueRef<ValueBox<T, D>>
namespace ValueCell {
export function create<T, D = never>(value: T, metadata?: D): ValueCell<T, D> {
return ValueRef.create(ValueBox.create(value, metadata));
}
/** The box.metadata is carried over from the old box */
export function update<T, D>(cell: ValueCell<T, D>, value: T): ValueCell<T, D> {
return ValueRef.set(cell, ValueBox.withValue(cell.ref, value));
}
export function set<T, D>(cell: ValueCell<T, D>, box: ValueBox<T, D>): ValueCell<T, D> {
return ValueRef.set(cell, box);
}
/** Updates the cell if the value is has changed, comparing by reference */
export function updateIfChanged<T, D>(cell: ValueCell<T, D>, value: T): ValueCell<T, D> {
return cell.ref.value !== value ? update(cell, value) : cell
}
}
export { ValueRef, ValueBox, ValueCell };