forked from MapComplete/MapComplete
Refactoring: dismantle 'inputHelpers'
This commit is contained in:
parent
29dc7d1e03
commit
7c42758b42
26 changed files with 485 additions and 439 deletions
|
|
@ -1,14 +1,9 @@
|
|||
import BaseUIElement from "../UI/BaseUIElement"
|
||||
import { Denomination } from "./Denomination"
|
||||
import UnitConfigJson from "./ThemeConfig/Json/UnitConfigJson"
|
||||
import unit from "../../assets/layers/unit/unit.json"
|
||||
import TagRenderingConfig from "./ThemeConfig/TagRenderingConfig"
|
||||
import Validators, { ValidatorType } from "../UI/InputElement/Validators"
|
||||
import { Validator } from "../UI/InputElement/Validator"
|
||||
import FloatValidator from "../UI/InputElement/Validators/FloatValidator"
|
||||
|
||||
export class Unit {
|
||||
private static allUnits = this.initUnits()
|
||||
public readonly appliesToKeys: Set<string>
|
||||
public readonly denominations: Denomination[]
|
||||
public readonly denominationsSorted: Denomination[]
|
||||
|
|
@ -85,226 +80,7 @@ export class Unit {
|
|||
}
|
||||
}
|
||||
|
||||
static fromJson(
|
||||
json:
|
||||
| UnitConfigJson
|
||||
| Record<
|
||||
string,
|
||||
string | { quantity: string; denominations: string[]; inverted?: boolean }
|
||||
>,
|
||||
tagRenderings: TagRenderingConfig[],
|
||||
ctx: string
|
||||
): Unit[] {
|
||||
const types: Record<string, ValidatorType> = {}
|
||||
for (const tagRendering of tagRenderings) {
|
||||
if (tagRendering.freeform?.type) {
|
||||
types[tagRendering.freeform.key] = tagRendering.freeform.type
|
||||
}
|
||||
}
|
||||
|
||||
if (!json.appliesToKey && !json.quantity) {
|
||||
return this.loadFromLibrary(<any>json, types, ctx)
|
||||
}
|
||||
return this.parse(<UnitConfigJson>json, types, ctx)
|
||||
}
|
||||
|
||||
private static parseDenomination(
|
||||
json: UnitConfigJson,
|
||||
validator: Validator,
|
||||
appliesToKey: string,
|
||||
ctx: string
|
||||
): Unit {
|
||||
const applicable = json.applicableUnits.map((u, i) =>
|
||||
Denomination.fromJson(u, validator, `${ctx}.units[${i}]`)
|
||||
)
|
||||
|
||||
if (
|
||||
json.defaultInput &&
|
||||
!applicable.some((denom) => denom.canonical.trim() === json.defaultInput)
|
||||
) {
|
||||
throw `${ctx}: no denomination has the specified default denomination. The default denomination is '${
|
||||
json.defaultInput
|
||||
}', but the available denominations are ${applicable
|
||||
.map((denom) => denom.canonical)
|
||||
.join(", ")}`
|
||||
}
|
||||
|
||||
return new Unit(
|
||||
json.quantity ?? "",
|
||||
appliesToKey === undefined ? undefined : [appliesToKey],
|
||||
applicable,
|
||||
json.eraseInvalidValues ?? false,
|
||||
validator
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* // Should detect invalid defaultInput
|
||||
* let threwError = false
|
||||
* try{
|
||||
* Unit.parse({
|
||||
* appliesToKey: ["length"],
|
||||
* defaultInput: "xcm",
|
||||
* applicableUnits: [
|
||||
* {
|
||||
* canonicalDenomination: "m",
|
||||
* useIfNoUnitGiven: true,
|
||||
* human: "meter"
|
||||
* }
|
||||
* ]
|
||||
* },"test")
|
||||
* }catch(e){
|
||||
* threwError = true
|
||||
* }
|
||||
* threwError // => true
|
||||
*
|
||||
* // Should work
|
||||
* Unit.parse({
|
||||
* appliesToKey: ["length"],
|
||||
* defaultInput: "xcm",
|
||||
* applicableUnits: [
|
||||
* {
|
||||
* canonicalDenomination: "m",
|
||||
* useIfNoUnitGiven: true,
|
||||
* humen: "meter"
|
||||
* },
|
||||
* {
|
||||
* canonicalDenomination: "cm",
|
||||
* human: "centimeter"
|
||||
* }
|
||||
* ]
|
||||
* }, "test")
|
||||
*/
|
||||
private static parse(
|
||||
json: UnitConfigJson,
|
||||
types: Record<string, ValidatorType>,
|
||||
ctx: string
|
||||
): Unit[] {
|
||||
const appliesTo = json.appliesToKey
|
||||
for (let i = 0; i < (appliesTo ?? []).length; i++) {
|
||||
const key = appliesTo[i]
|
||||
if (key.trim() !== key) {
|
||||
throw `${ctx}.appliesToKey[${i}] is invalid: it starts or ends with whitespace`
|
||||
}
|
||||
}
|
||||
|
||||
if ((json.applicableUnits ?? []).length === 0) {
|
||||
throw `${ctx}: define at least one applicable unit`
|
||||
}
|
||||
// Some keys do have unit handling
|
||||
|
||||
const units: Unit[] = []
|
||||
if (appliesTo === undefined) {
|
||||
units.push(this.parseDenomination(json, Validators.get("float"), undefined, ctx))
|
||||
}
|
||||
for (const key of appliesTo ?? []) {
|
||||
const validator = Validators.get(types[key] ?? "float")
|
||||
units.push(this.parseDenomination(json, validator, undefined, ctx))
|
||||
}
|
||||
return units
|
||||
}
|
||||
|
||||
private static initUnits(): Map<string, Unit> {
|
||||
const m = new Map<string, Unit>()
|
||||
const units = (<UnitConfigJson[]>unit.units).flatMap((json, i) =>
|
||||
this.parse(json, {}, "unit.json.units." + i)
|
||||
)
|
||||
|
||||
for (const unit of units) {
|
||||
m.set(unit.quantity, unit)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
private static getFromLibrary(name: string, ctx: string): Unit {
|
||||
const loaded = this.allUnits.get(name)
|
||||
if (loaded === undefined) {
|
||||
throw (
|
||||
"No unit with quantity name " +
|
||||
name +
|
||||
" found (at " +
|
||||
ctx +
|
||||
"). Try one of: " +
|
||||
Array.from(this.allUnits.keys()).join(", ")
|
||||
)
|
||||
}
|
||||
return loaded
|
||||
}
|
||||
|
||||
private static loadFromLibrary(
|
||||
spec: Record<
|
||||
string,
|
||||
| string
|
||||
| { quantity: string; denominations: string[]; canonical?: string; inverted?: boolean }
|
||||
>,
|
||||
types: Record<string, ValidatorType>,
|
||||
ctx: string
|
||||
): Unit[] {
|
||||
const units: Unit[] = []
|
||||
for (const key in spec) {
|
||||
const toLoad = spec[key]
|
||||
const validator = Validators.get(types[key] ?? "float")
|
||||
if (typeof toLoad === "string") {
|
||||
const loaded = this.getFromLibrary(toLoad, ctx)
|
||||
units.push(
|
||||
new Unit(
|
||||
loaded.quantity,
|
||||
[key],
|
||||
loaded.denominations,
|
||||
loaded.eraseInvalid,
|
||||
validator,
|
||||
toLoad["inverted"]
|
||||
)
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
const loaded = this.getFromLibrary(toLoad.quantity, ctx)
|
||||
const quantity = toLoad.quantity
|
||||
|
||||
const fetchDenom = (d: string): Denomination => {
|
||||
const found = loaded.denominations.find(
|
||||
(denom) => denom.canonical.toLowerCase() === d
|
||||
)
|
||||
if (!found) {
|
||||
throw (
|
||||
`Could not find a denomination \`${d}\`for quantity ${quantity} at ${ctx}. Perhaps you meant to use on of ` +
|
||||
loaded.denominations.map((d) => d.canonical).join(", ")
|
||||
)
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
if (!Array.isArray(toLoad.denominations)) {
|
||||
throw (
|
||||
"toLoad is not an array. Did you forget the [ and ] around the denominations at " +
|
||||
ctx +
|
||||
"?"
|
||||
)
|
||||
}
|
||||
const denoms = toLoad.denominations
|
||||
.map((d) => d.toLowerCase())
|
||||
.map((d) => fetchDenom(d))
|
||||
.map((d) => d.withValidator(validator))
|
||||
|
||||
if (toLoad.canonical) {
|
||||
const canonical = fetchDenom(toLoad.canonical).withValidator(validator)
|
||||
denoms.unshift(canonical.withBlankCanonical())
|
||||
}
|
||||
units.push(
|
||||
new Unit(
|
||||
loaded.quantity,
|
||||
[key],
|
||||
denoms,
|
||||
loaded.eraseInvalid,
|
||||
validator,
|
||||
toLoad["inverted"]
|
||||
)
|
||||
)
|
||||
}
|
||||
return units
|
||||
}
|
||||
|
||||
isApplicableToKey(key: string | undefined): boolean {
|
||||
if (key === undefined) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue