@freemework/common
Version:
Common library of the Freemework Project.
366 lines (329 loc) • 10.7 kB
text/typescript
import { FException } from "../exception/index.js";
import { FDecimal } from "../primitive/index.js";
export abstract class FConfigurationValue {
/**
* Factory constructor
*/
public static factory(key: string, value: string | null, sourceURI: URL | null, overriden: FConfigurationValue | null): FConfigurationValue {
return new _FConfigurationValue(key, value, sourceURI, overriden);
}
/**
* Configuration source URI.
*
* See `sourceURI` in `FConfiguration` for details.
*/
public abstract get sourceURI(): URL | null;
/**
* Reference to an overriden value in chain configuration.
*/
public abstract get overridden(): FConfigurationValue | null;
/**
* Get `true` if value is `null` (configuration key presents but no value)
*/
public abstract get isNull(): boolean;
/**
* Key of current value (from root namespace)
*/
public abstract get key(): string;
public abstract get asBase64(): Uint8Array;
public abstract get asBase64Nullable(): Uint8Array | null;
public abstract get asBoolean(): boolean;
public abstract get asBooleanNullable(): boolean | null;
public abstract get asDateIso8601(): Date;
public abstract get asDateIso8601Nullable(): Date | null;
public abstract get asDateTimestamp(): Date;
public abstract get asDateTimestampNullable(): Date | null;
public abstract get asDecimal(): FDecimal;
public abstract get asDecimalNullable(): FDecimal | null;
public abstract get asInteger(): number;
public abstract get asIntegerNullable(): number | null;
public abstract get asIntegerNegative(): number;
public abstract get asIntegerNegativeNullable(): number | null;
public abstract get asIntegerPositive(): number;
public abstract get asIntegerPositiveNullable(): number | null;
public abstract get asNumber(): number;
public abstract get asNumberNullable(): number | null;
/**
* Gets value representations as TCP port
*/
public abstract get asPortNumber(): number;
/**
* Gets value representations as TCP port or `null` if value empty
*/
public abstract get asPortNumberNullable(): number | null;
public abstract get asString(): string;
public abstract get asStringNullable(): string | null;
public abstract get asUrl(): URL;
public abstract get asUrlNullable(): URL | null;
}
import { FConfigurationValueException } from "./f_configuration_value_exception.js";
class _FConfigurationValue extends FConfigurationValue {
public get sourceURI(): URL | null { return this._sourceURI; }
public get overridden(): FConfigurationValue | null { return this._overridden; }
public get key(): string { return this._key; }
public get isNull(): boolean { return this._value === null; }
public get asBase64(): Uint8Array {
this.assertNotNullValue(this._value, "asBase64");
return this.fromBase64(this._value);
}
public get asBase64Nullable(): Uint8Array | null {
if (this._value === null) { return null; }
return this.fromBase64(this._value);
}
public get asBoolean(): boolean {
this.assertNotNullValue(this._value, "asBoolean");
return this.fromBoolean(this._value);
}
public get asBooleanNullable(): boolean | null {
if (this._value === null) { return null; }
return this.fromBoolean(this._value);
}
public get asDateIso8601(): Date {
this.assertNotNullValue(this._value, "asDateIso8601");
return this.fromDateIso8601(this._value);
}
public get asDateIso8601Nullable(): Date | null {
if (this._value === null) { return null; }
return this.fromDateIso8601(this._value);
}
public get asDateTimestamp(): Date {
this.assertNotNullValue(this._value, "asDateTimestamp");
return this.fromDateTimestamp(this._value);
}
public get asDateTimestampNullable(): Date | null {
if (this._value === null) { return null; }
return this.fromDateTimestamp(this._value);
}
public get asDecimal(): FDecimal {
this.assertNotNullValue(this._value, "asDecimal");
return this.fromDecimal(this._value);
}
public get asDecimalNullable(): FDecimal | null {
if (this._value === null) { return null; }
return this.fromDecimal(this._value);
}
public get asInteger(): number {
this.assertNotNullValue(this._value, "asInteger");
return this.fromInteger(this._value);
}
public get asIntegerNullable(): number | null {
if (this._value === null) { return null; }
return this.fromInteger(this._value);
}
public get asIntegerNegative(): number {
this.assertNotNullValue(this._value, "asIntegerNegative");
return this.fromIntegerNegative(this._value);
}
public get asIntegerNegativeNullable(): number | null {
if (this._value === null) { return null; }
return this.fromIntegerNegative(this._value);
}
public get asIntegerPositive(): number {
this.assertNotNullValue(this._value, "asIntegerPositive");
return this.fromIntegerPositive(this._value);
}
public get asIntegerPositiveNullable(): number | null {
if (this._value === null) { return null; }
return this.fromIntegerPositive(this._value);
}
public get asNumber(): number {
this.assertNotNullValue(this._value, "asNumber");
return this.fromNumber(this._value);
}
public get asNumberNullable(): number | null {
if (this._value === null) { return null; }
return this.fromNumber(this._value);
}
public get asPortNumber(): number {
this.assertNotNullValue(this._value, "asPortNumber");
return this.fromPortNumber(this._value);
}
public get asPortNumberNullable(): number | null {
if (this._value === null) { return null; }
return this.fromPortNumber(this._value);
}
public get asString(): string {
this.assertNotNullValue(this._value, "asString");
return this.fromString(this._value);
}
public get asStringNullable(): string | null {
if (this._value === null) { return null; }
return this.fromString(this._value);
}
public get asUrl(): URL {
this.assertNotNullValue(this._value, "asUrl");
return this.fromUrl(this._value);
}
public get asUrlNullable(): URL | null {
if (this._value === null) { return null; }
return this.fromUrl(this._value);
}
public constructor(
private readonly _key: string,
private readonly _value: string | null,
private readonly _sourceURI: URL | null,
private readonly _overridden: FConfigurationValue | null
) {
super();
}
public toJSON() {
return this._value;
}
private assertNotNullValue(
value: string | null, callerProperty: Exclude<keyof FConfigurationValue, "sourceURI" | "overridden" | "key">
): asserts value is string {
if (value === null) {
throw new FConfigurationValueException(
this,
`Cannot represent null value ${callerProperty}`,
this.key
);
}
}
private fromBase64(value: string): Uint8Array {
const parsedData = Buffer.from(value, "base64");
const restoredValue = parsedData.toString("base64");
if (restoredValue !== value) {
const partOfValue = value.slice(0, 4);
const maskValue = `${partOfValue}...`;
throw new FConfigurationValueException(
this,
`Cannot parse value '${maskValue}' as base64.`,
this.key,
);
}
return parsedData;
}
private fromBoolean(value: string): boolean {
const lowerCaseValue: string = value.toLowerCase();
switch (lowerCaseValue) {
case "true":
case "enabled":
case "yes":
return true;
case "false":
case "disabled":
case "no":
return false;
default:
throw new FConfigurationValueException(
this,
`Cannot convert the value '${value}' to boolean type.`,
this.key,
);
}
}
private fromDateIso8601(value: string): Date {
const date = new Date(value);
if (date.toString() === "Invalid Date") {
throw new FConfigurationValueException(
this,
`Cannot parse value '${value}' as Date ISO8601. Invalid Date.`,
this.key
);
}
return date;
}
private fromDateTimestamp(value: string): Date {
const friendlyValue: number = Number.parseInt(value, 10);
if (friendlyValue.toString() !== value) {
throw new FConfigurationValueException(
this,
`Cannot parse value '${value}' as Date timestamp. Unparsable timestamp.`,
this.key,
);
}
const date = new Date(friendlyValue);
if (date.toString() === "Invalid Date") {
throw new FConfigurationValueException(
this,
`Cannot parse value '${value}' as Date timestamp. Invalid Date.`,
this.key
);
}
return date;
}
private fromDecimal(value: string): FDecimal {
try {
return FDecimal.parse(value);
} catch (e) {
throw new FConfigurationValueException(
this,
`Cannot parse value '${value}' as FDecimal.`,
this.key,
FException.wrapIfNeeded(e)
);
}
}
private fromInteger(value: string): number {
const friendlyValue = Number.parseInt(value, 10);
if (friendlyValue.toString() === value) { return friendlyValue; }
throw new FConfigurationValueException(
this,
`Cannot convert the value '${value}' to integer type.`,
this.key,
);
}
private fromIntegerNegative(value: string): number {
const friendlyValue = this.fromInteger(value);
if (friendlyValue < 0) { return friendlyValue; }
throw new FConfigurationValueException(
this,
`Cannot convert the value '${value}' to integer negative type.`,
this.key,
);
}
private fromIntegerPositive(value: string): number {
const friendlyValue = this.fromInteger(value);
if (friendlyValue > 0) { return friendlyValue; }
throw new FConfigurationValueException(
this,
`Cannot convert the value '${value}' to integer positive type.`,
this.key,
);
}
private fromNumber(value: string): number {
const friendlyValue = Number.parseFloat(value);
if (friendlyValue.toString() === value) { return friendlyValue; }
throw new FConfigurationValueException(
this,
`Cannot convert the value '${value}' to float type.`,
this.key
);
}
private fromPortNumber(value: string): number {
const friendlyValue = Number.parseInt(value);
if (friendlyValue.toString() !== value) {
throw new FConfigurationValueException(
this,
`Cannot convert the value '${value}' to TCP port number.`,
this.key
);
}
if (friendlyValue < 0 || friendlyValue > 65535) {
throw new FConfigurationValueException(
this,
`Cannot convert the value '${value}' to TCP port number. Value out of range 0 - 65535.`,
this.key
);
}
return friendlyValue;
}
private fromString(value: string): string {
return value;
}
private fromUrl(value: string): URL {
try {
return new URL(value);
} catch (e) {
const partOfValue = value.slice(0, 4);
const maskValue = `${partOfValue}...`;
throw new FConfigurationValueException(
this,
`Cannot parse value '${maskValue}' as URL.`,
this.key,
FException.wrapIfNeeded(e)
);
}
}
}