import { matcher } from "../tag_utils.js";
import { random } from "../random_utils.js";
/**
* A generic database class for storing and querying models. Subclasses can implement
* more specific logic around each model (particularly, the code to convert
* configuration data into specific models...each model type can have its own compact
* serialization format).
* @class Database
*/
class Database {
/**
* Creates a new Database instance.
* @param {Object} [options={}] - Configuration options
* @param {Function} [options.parser] - Parser function to process configuration data
* before adding it to the database. If not provided, configuration is passed
* directly to the model
*/
constructor({ parser = (m) => m } = {}) {
/**
* Array of models stored in the database.
* @type {Array}
*/
this.models = [];
/**
* Parser function used to process data before passing it to the model constructor
* @type {Function}
*/
this.parser = parser;
}
/**
* Adds one or more data items to the database.
* @param {...*} data - Data items to add to the database
* @returns {Database} Returns this instance for method chaining
*
* @example
* database.add(item1, item2, item3);
*/
add(...data) {
for (let i = 0, len = data.length; i < len; i++) {
var records = this.parser(data[i]);
records.forEach((r) => (this.models[this.models.length] = r));
}
return this;
}
/**
* Finds all models that match the specified criteria. Subclasses may add additional
* search parameters, but all models support searching by tags.
*
* @param {Object} [options={}] - Search criteria
* @param {string} [options.tags] - Tag filter string
* @returns {Array} Array of matching models
*
* @example
* database.findAll({ tags: "weapon firearm" });
*/
findAll({ tags } = {}) {
return this.models.filter((m) => tagFilter(m, tags));
}
/**
* Finds a random model that matches the specified criteria. Subclasses may add
* additional search parameters, but all models support searching by tags.
*
* @param {Object} [options={}] - Search criteria
* @param {string} [options.tags] - Tag filter string
* @returns {*} A random matching model, or undefined if none found
*
* @example
* database.findOne({ tags: "weapon firearm" });
*/
findOne({ tags } = {}) {
return random(this.findAll({ tags }));
}
}
export default Database;
/**
* Filter helper functions for creating consistent sorting criteria in database subclasses.
* These functions should be tested with && expressions between them for consistency
* (all supplied criteria must match, but if nothing is supplied, no match is made).
*/
/**
* Filters models by ID.
* @function idFilter
* @param {Object} m - The model to test
* @param {*} id - The ID to match against (undefined means no filtering)
* @returns {boolean} True if the model matches or no ID filter is specified
*
* @example
* models.filter(m => idFilter(m, "item123"));
*/
export function idFilter(m, id) {
return typeof id === "undefined" ? true : m.id === id;
}
/**
* Filters models by type (case-insensitive).
* @function typeFilter
* @param {Object} m - The model to test
* @param {string} type - The type to match against (undefined means no filtering)
* @returns {boolean} True if the model matches or no type filter is specified
*
* @example
* models.filter(m => typeFilter(m, "weapon"));
*/
export function typeFilter(m, type) {
return typeof type === "undefined" ? true : m.type.toLowerCase() === type.toLowerCase();
}
/**
* Filters models by tags using tag matching logic.
* @function tagFilter
* @param {Object} m - The model to test
* @param {string} tags - The tag filter string (undefined means no filtering)
* @returns {boolean} True if the model matches or no tag filter is specified
*
* @example
* models.filter(m => tagFilter(m, "weapon firearm"));
*/
export function tagFilter(m, tags) {
return typeof tags === "undefined" ? true : matcher(tags, m.tags);
}
/**
* Filters models by checking if a name exists in the model's names array.
* @function namesFilter
* @param {Object} m - The model to test
* @param {string} name - The name to search for (undefined means no filtering)
* @returns {boolean} True if the model contains the name or no name filter is specified
*
* @example
* models.filter(m => namesFilter(m, "John"));
*/
export function namesFilter(m, name) {
return typeof name === "undefined" ? true : m.names.includes(name);
}