/** HED schema loading functions. */
/* Imports */
import xml2js from 'xml2js'
import * as files from '../utils/files'
import { IssueError } from '../issues/issues'
import { localSchemaList } from './config'
/**
* Load schema XML data from a schema version or path description.
*
* @param {SchemaSpec} schemaDef The description of which schema to use.
* @returns {Promise<Object>} The schema XML data.
* @throws {IssueError} If the schema could not be loaded.
*/
export default async function loadSchema(schemaDef = null) {
const xmlData = await loadPromise(schemaDef)
if (xmlData === null) {
IssueError.generateAndThrow('invalidSchemaSpecification', { spec: JSON.stringify(schemaDef) })
}
return xmlData
}
/**
* Choose the schema Promise from a schema version or path description.
*
* @param {SchemaSpec} schemaDef The description of which schema to use.
* @returns {Promise<Object>} The schema XML data.
* @throws {IssueError} If the schema could not be loaded.
*/
async function loadPromise(schemaDef) {
if (schemaDef === null) {
return null
} else if (schemaDef.localPath) {
return loadLocalSchema(schemaDef.localPath)
} else if (localSchemaList.has(schemaDef.localName)) {
return loadBundledSchema(schemaDef)
} else {
return loadRemoteSchema(schemaDef)
}
}
/**
* Load schema XML data from the HED GitHub repository.
*
* @param {SchemaSpec} schemaDef The standard schema version to load.
* @returns {Promise<object>} The schema XML data.
* @throws {IssueError} If the schema could not be loaded.
*/
function loadRemoteSchema(schemaDef) {
let url
if (schemaDef.library) {
url = `https://raw.githubusercontent.com/hed-standard/hed-schemas/refs/heads/main/library_schemas/${schemaDef.library}/hedxml/HED_${schemaDef.library}_${schemaDef.version}.xml`
} else {
url = `https://raw.githubusercontent.com/hed-standard/hed-schemas/refs/heads/main/standard_schema/hedxml/HED${schemaDef.version}.xml`
}
return loadSchemaFile(files.readHTTPSFile(url), 'remoteSchemaLoadFailed', { spec: JSON.stringify(schemaDef) })
}
/**
* Load schema XML data from a local file.
*
* @param {string} path The path to the schema XML data.
* @returns {Promise<object>} The schema XML data.
* @throws {IssueError} If the schema could not be loaded.
*/
function loadLocalSchema(path) {
return loadSchemaFile(files.readFile(path), 'localSchemaLoadFailed', { path: path })
}
/**
* Load schema XML data from a bundled file.
*
* @param {SchemaSpec} schemaDef The description of which schema to use.
* @returns {Promise<object>} The schema XML data.
* @throws {IssueError} If the schema could not be loaded.
*/
async function loadBundledSchema(schemaDef) {
try {
return parseSchemaXML(localSchemaList.get(schemaDef.localName))
} catch (error) {
const issueArgs = { spec: JSON.stringify(schemaDef), error: error.message }
IssueError.generateAndThrow('bundledSchemaLoadFailed', issueArgs)
}
}
/**
* Actually load the schema XML file.
*
* @param {Promise<string>} xmlDataPromise The Promise containing the unparsed XML data.
* @param {string} issueCode The issue code.
* @param {Object<string, string>} issueArgs The issue arguments passed from the calling function.
* @returns {Promise<object>} The parsed schema XML data.
* @throws {IssueError} If the schema could not be loaded.
*/
async function loadSchemaFile(xmlDataPromise, issueCode, issueArgs) {
try {
const data = await xmlDataPromise
return parseSchemaXML(data)
} catch (error) {
issueArgs.error = error.message
IssueError.generateAndThrow(issueCode, issueArgs)
}
}
/**
* Parse the schema XML data.
*
* @param {string} data The XML data.
* @returns {Promise<object>} The schema XML data.
*/
function parseSchemaXML(data) {
return xml2js.parseStringPromise(data, { explicitCharkey: true })
}