Skip to content
Snippets Groups Projects
API_Parsers.ts 3.17 KiB
// A hack to make a list of strings on type level. 
// If you add another type to be parsable i.e. implement a parser for it, also add its name here
const primitives = ["string", "MathMLElement", "HTMLElement", "SVGElement"] as const
type Primitive = string | MathMLElement | HTMLElement | SVGElement
type Parsable_Primitive = { parseAs: typeof primitives[number], content: string };
function is_primitive(a: any): a is Parsable_Primitive {
    return "parseAs" in a
        && primitives.includes(a.parseAs)
        && "content" in a
        && typeof a.content === "string"
}

// type Typed_JSONObject = { [data: string]: Primitive | Backend_Object }


// declare var Generic_BackendObject: {
//     label: MathMLElement
//     uri: string
//     type: string
//     [data: string]: any
//     prototype: Backend_Object;
// };


type Parsable_BO = { [key: string]: Parsable_Primitive | Parsable_BO | [Parsable_BO] };

const parser = new DOMParser;
// A shorthand we will need several times
function parse (s: string): Primitive {
    const doc = parser.parseFromString(s, "text/html")
    const errorNode = doc.querySelector("parsererror");
    if (errorNode) {
        console.error(errorNode)
        throw{}
    } else {
        return doc.body.firstChild as Primitive
    }
}

/**
 * 
 * @param json_object 
 * @returns 
 * @throws TypeError if {@link json_object} doesn't adhere to the JSON schema
 */
function backendObject_fromJSON(json_object: Parsable_BO): Backend_Object {
    // the Backend_Object to be
    let bo: {[data:string]: any} = {}

    // If the input is sensible this works, otherwise we have to throw a TypeError anyway
    for (let i in json_object) {
        const member = json_object[i]
        //console.log(member)
        if (is_primitive(member)) {
            switch (member.parseAs) {
                case "string":
                    bo[i] = member.content
                    break;
                // all other cases are identical ()
                case "MathMLElement":
                case "HTMLElement":
                case "SVGElement":
                    //console.log(i, parse(member.content), bo)
                    bo[i] = parse(member.content)
                    break;
            }
        }
        else {
            if (Array.isArray(member)) {
                bo[i] = []
                member.forEach((value, index) => {
                    bo[i][index] = backendObject_fromJSON(value)
                })
            }
            else { bo[i] = backendObject_fromJSON(member) }
        }
    }
    if (!("label" in bo)) {
        console.error(`Attribute 'label' is missing. Cannot finish parsing`, json_object, bo)
        throw new TypeError
    }
    if (!("uri" in bo && typeof bo.uri === "string")) {
        console.error(`Attribute 'uri': ${bo.uri} is missing or not a string. Cannot finish parsing`, json_object, bo)
        throw new TypeError
    }
    if (!("type" in bo && typeof bo.type === "string")) {
        console.error(`Attribute 'type': ${bo.type} is missing or not a string. Cannot finish parsing`, json_object, bo)
        throw new TypeError
    }
    // for some reason ts does not believe the conditions above
    return bo as unknown as Backend_Object

}