145 lines
3.2 KiB
JavaScript
145 lines
3.2 KiB
JavaScript
// Note: Only handled objects and primitives are allowed to be set/pushed/mapped
|
|
|
|
import { inspect } from 'node:util';
|
|
|
|
class Not_Storable_Error extends Error {
|
|
constructor(data) {
|
|
const { storage, value, property, id } = data;
|
|
const type = value === null ? 'null' : typeof value;
|
|
const location = property ? `property "${property}" of record #${id}` : `record #${id}`;
|
|
super(`Cannot store value of type "${type}" in ${location}: ${inspect(value)}`);
|
|
this.data = data;
|
|
}
|
|
}
|
|
|
|
class Not_Mutable_Error extends Error {
|
|
constructor(data) {
|
|
const { storage, value, property, id } = data;
|
|
const type = value === null ? 'null' : typeof value;
|
|
const location = property ? `property "${property}" of record #${id}` : `record #${id}`;
|
|
super(`Cannot set ${location}: record is immutable (tried to store value of type "${type}": ${inspect(value)})`);
|
|
this.data = data;
|
|
}
|
|
}
|
|
|
|
class No_Such_Object_Index_Error extends Error {
|
|
constructor(data) {
|
|
const { storage, property, id } = data;
|
|
const location = property ? `property "${property}" of record #${id}` : `record #${id}`;
|
|
super(`Cannot get ${location}: no such record index`);
|
|
this.data = data;
|
|
}
|
|
}
|
|
|
|
|
|
function load_descriptor({ type, value }, { storage, mutable }) {
|
|
if (type === 'primitive') {
|
|
return value;
|
|
} else {
|
|
throw new Error('not implemented');
|
|
}
|
|
}
|
|
|
|
const Record_Handler = {
|
|
|
|
get(target, property, receiver) {
|
|
const { storage, id } = target;
|
|
return storage.get_record_property(id, property);
|
|
},
|
|
|
|
set(target, property, value) {
|
|
const { storage, id, mutable } = target;
|
|
|
|
if (!mutable) {
|
|
throw new Not_Mutable_Error({ storage, id, property, value });
|
|
}
|
|
|
|
storage.set_record_property(id, property, value);
|
|
return true;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
class Storage {
|
|
#pending_id = 1;
|
|
#records = new Map();
|
|
|
|
create_record(data=undefined, mutable=true) {
|
|
const id = this.acquire_new_id();
|
|
const property_bag = {};
|
|
const result = new Proxy({
|
|
storage: this,
|
|
id,
|
|
property_bag,
|
|
mutable,
|
|
}, Record_Handler);
|
|
|
|
this.#records.set(id, property_bag);
|
|
|
|
if (data !== undefined) {
|
|
Object.assign(result, data);
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
get_record_property(item_id, property) {
|
|
|
|
const property_bag = this.#records.get(item_id);
|
|
if (property_bag === undefined) {
|
|
throw new No_Such_Object_Index_Error({storage: this, id: item_id, property});
|
|
}
|
|
|
|
return property_bag[property];
|
|
|
|
}
|
|
|
|
set_record_property(item_id, property, value) {
|
|
|
|
const descriptor = this.maybe_create_descriptor(value);
|
|
|
|
if (!descriptor) {
|
|
throw new Not_Storable_Error({ storage: this, id: item_id, property, value });
|
|
}
|
|
|
|
const property_bag = this.#records.get(item_id);
|
|
if (property_bag === undefined) {
|
|
throw new No_Such_Object_Index_Error({storage: this, id: item_id, property});
|
|
}
|
|
|
|
property_bag[property] = descriptor;
|
|
}
|
|
|
|
maybe_create_descriptor(value) {
|
|
if (typeof value === 'string') {
|
|
return { type: 'primitive', value };
|
|
}
|
|
}
|
|
|
|
can_be_stored(value) {
|
|
if (typeof value === 'string') {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
acquire_new_id() {
|
|
return this.#pending_id++;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
const s1 = new Storage();
|
|
|
|
|
|
const p1 = s1.create_record({stuff: 5n}, true);
|
|
console.log(p1.blargh);
|
|
|
|
console.log(s1.get_record_property(1, 'stuff'))
|
|
|
|
|
|
//p1.stuff = ['hello', 'world'];
|