diamante-js-xdr
Version:
Read/write XDR encoded data structures (RFC 4506)
164 lines (137 loc) • 4.2 kB
JavaScript
import { Void } from './void';
import { Reference } from './reference';
import { XdrCompositeType } from './xdr-type';
import { XdrWriterError } from './errors';
export class Union extends XdrCompositeType {
constructor(aSwitch, value) {
super();
this.set(aSwitch, value);
}
set(aSwitch, value) {
if (typeof aSwitch === 'string') {
aSwitch = this.constructor._switchOn.fromName(aSwitch);
}
this._switch = aSwitch;
const arm = this.constructor.armForSwitch(this._switch);
this._arm = arm;
this._armType = arm === Void ? Void : this.constructor._arms[arm];
this._value = value;
}
get(armName = this._arm) {
if (this._arm !== Void && this._arm !== armName)
throw new TypeError(`${armName} not set`);
return this._value;
}
switch() {
return this._switch;
}
arm() {
return this._arm;
}
armType() {
return this._armType;
}
value() {
return this._value;
}
static armForSwitch(aSwitch) {
const member = this._switches.get(aSwitch);
if (member !== undefined) {
return member;
}
if (this._defaultArm) {
return this._defaultArm;
}
throw new TypeError(`Bad union switch: ${aSwitch}`);
}
static armTypeForArm(arm) {
if (arm === Void) {
return Void;
}
return this._arms[arm];
}
/**
* @inheritDoc
*/
static read(reader) {
const aSwitch = this._switchOn.read(reader);
const arm = this.armForSwitch(aSwitch);
const armType = arm === Void ? Void : this._arms[arm];
let value;
if (armType !== undefined) {
value = armType.read(reader);
} else {
value = arm.read(reader);
}
return new this(aSwitch, value);
}
/**
* @inheritDoc
*/
static write(value, writer) {
if (!(value instanceof this))
throw new XdrWriterError(`${value} is not a ${this.unionName}`);
this._switchOn.write(value.switch(), writer);
value.armType().write(value.value(), writer);
}
/**
* @inheritDoc
*/
static isValid(value) {
return value instanceof this;
}
static create(context, name, config) {
const ChildUnion = class extends Union {};
ChildUnion.unionName = name;
context.results[name] = ChildUnion;
if (config.switchOn instanceof Reference) {
ChildUnion._switchOn = config.switchOn.resolve(context);
} else {
ChildUnion._switchOn = config.switchOn;
}
ChildUnion._switches = new Map();
ChildUnion._arms = {};
// resolve default arm
let defaultArm = config.defaultArm;
if (defaultArm instanceof Reference) {
defaultArm = defaultArm.resolve(context);
}
ChildUnion._defaultArm = defaultArm;
for (const [aSwitch, armName] of config.switches) {
const key =
typeof aSwitch === 'string'
? ChildUnion._switchOn.fromName(aSwitch)
: aSwitch;
ChildUnion._switches.set(key, armName);
}
// add enum-based helpers
// NOTE: we don't have good notation for "is a subclass of XDR.Enum",
// and so we use the following check (does _switchOn have a `values`
// attribute) to approximate the intent.
if (ChildUnion._switchOn.values !== undefined) {
for (const aSwitch of ChildUnion._switchOn.values()) {
// Add enum-based constructors
ChildUnion[aSwitch.name] = function ctr(value) {
return new ChildUnion(aSwitch, value);
};
// Add enum-based "set" helpers
ChildUnion.prototype[aSwitch.name] = function set(value) {
return this.set(aSwitch, value);
};
}
}
if (config.arms) {
for (const [armsName, value] of Object.entries(config.arms)) {
ChildUnion._arms[armsName] =
value instanceof Reference ? value.resolve(context) : value;
// Add arm accessor helpers
if (value !== Void) {
ChildUnion.prototype[armsName] = function get() {
return this.get(armsName);
};
}
}
}
return ChildUnion;
}
}