/** * This file is part of Haketilo. * * Function: Operations on resources and mappings. * * Copyright (C) 2021 Wojtek Kosior * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * As additional permission under GNU GPL version 3 section 7, you * may distribute forms of that code without the copy of the GNU * GPL normally required by section 4, provided you include this * license notice and, in case of non-source distribution, a URL * through which recipients can access the Corresponding Source. * If you modify file(s) with this exception, you may extend this * exception to your version of the file(s), but you are not * obligated to do so. If you do not wish to do so, delete this * exception statement from your version. * * As a special exception to the GPL, any HTML file which merely * makes function calls to this code, and for that purpose * includes it by reference shall be deemed a separate work for * copyright law purposes. If you modify this code, you may extend * this exception to your version of the code, but you are not * obligated to do so. If you do not wish to do so, delete this * exception statement from your version. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * I, Wojtek Kosior, thereby promise not to sue for violation of this file's * license. Although I request that you do not make use of this code in a * proprietary program, I am not going to enforce this in court. */ /* * Convert ver_str into an array representation, e.g. for ver_str="4.6.13.0" * return [4, 6, 13, 0]. */ const parse_version = ver_str => ver_str.split(".").map(n => parseInt(n)); /* * ver is an array of integers. rev is an optional integer. Produce string * representation of version (optionally with revision number), like: * 1.2.3-5 * No version normalization is performed. */ const version_string = (ver, rev=0) => ver.join(".") + (rev ? `-${rev}` : ""); #EXPORT version_string /* * This function overloads on the number of arguments. If one argument is * passed, it is an item definition (it need not be complete, only identifier, * version and, if applicable, revision properties are relevant). If two or * three arguments are given, they are in order: item identifier, item version * and item revision. * Returned is a string identifying this version of item. */ function item_id_string(...args) { let def = args[0] if (args.length > 1) def = {identifier: args[0], version: args[1], revision: args[2]}; return !Array.isArray(def.version) ? def.identifier : `${def.identifier}-${version_string(def.version, def.revision)}`; } #EXPORT item_id_string /* vers should be an array of comparable values. Return the greatest one. */ const max = vals => vals.reduce((v1, v2) => v1 > v2 ? v1 : v2); /* * versioned_item should be a dict with keys being version strings and values * being definitions of the respective versions of a single resource/mapping. * Example: * { * "1": { * version: [1]//, * // more stuff * }, * "1.1": { * version: [1, 1]//, * // more stuff * } * } * * Returns the definition with the highest version. */ function get_newest_version(versioned_item) { const best_ver = max(Object.keys(versioned_item).map(parse_version)); return versioned_item[version_string(best_ver)]; } #EXPORT get_newest_version AS get_newest /* * item is a definition of a resource or mapping. Yield all file references * (objects with `file` and `sha256` properties) this definition has. */ function* get_used_files(item) { for (const file of item.source_copyright) yield file; if (item.type === "resource") { for (const file of item.scripts || []) yield file; } } #EXPORT get_used_files AS get_files /* * Function to parse URIs like: * https://hydrilla.koszko.org/schemas/api_mapping_description-2.schema.json */ const name_base_re = "([^/]*)"; const major_number_re = "([1-9][0-9]*)"; const minor_number_re = "(?:[1-9][0-9]*|0)"; const numbers_rest_re = `(?:\\.${minor_number_re})*`; const version_re = `(${major_number_re}${numbers_rest_re})`; const schema_name_re = `${name_base_re}-${version_re}\\.schema\\.json`; const schema_name_regex = new RegExp(schema_name_re); const schema_name_parts = ["full", "name_base", "version", "major"]; function parse_schema_uri(uri) { const match = schema_name_regex.exec(uri); if (!match) return match; const result = {}; for (let i = 0; i < schema_name_parts.length; i++) result[schema_name_parts[i]] = match[i]; return result } #EXPORT parse_schema_uri /* Extract the number that indicates entity's compatibility mode. */ function get_schema_major_version(instance) { return parseInt(parse_schema_uri(instance.$schema).major); } #EXPORT get_schema_major_version #IF NEVER /* * Note: the functions below were overeagerly written and are not used now but * might prove useful to once we add more functionalities and are hence kept... */ /* * Clone recursively all objects. Leave other items (arrays, strings) untouched. */ function deep_object_copy(object) { const orig = {object}; const result = {}; const to_copy = [[orig, {}]]; while (to_copy.length > 0) { const [object, copy] = to_copy.pop(); for (const [key, value] of Object.entries(object)) { copy[key] = value; if (typeof value === "object" && !Array.isArray(value)) { const value_copy = {}; to_copy.push([value, value_copy]); copy[key] = value_copy; } } } return result.orig; } /* helper function for normalize_version() */ const version_reductor = (acc, n) => [...(n || acc.length ? [n] : []), ...acc]; /* * ver is an array of integers. Strip right-most zeroes from ver. * * Returns a *new* array. Doesn't modify its argument. */ const normalize_version = ver => ver.reduceRight(version_reductor, []); #ENDIF