UNPKG

@ama-team/voxengine-sdk

Version:

Bundle of helper functions and classes for VoxEngine scenario scripts

321 lines (292 loc) 7.29 kB
/** * @module logger/_common */ /** * @enum * @readonly */ var Level = require('./Level').Level /** * This class represents basic tree branch consisting of a node and (optionally) * child nodes. It is created with sole purpose to provide easy support for * package hierarchy tooling, so `ama-team.voxengine` would be presented as node * (root) -> (ama-team) -> (voxengine), and it's properties would be stored as * trees, allowing easy and configurable overlapping. * * @class * * @template {V} Wrapped value type * * @param {V|null} value Initial value */ function TreeNode (value) { var self = this /** * @template {V} * @typedef {Object.<string, TreeNode<V>>} */ var children = {} /** * Adds new child to current node. * * @param {string} name Child name * @param {TreeNode<V>} child Child value * @return {TreeNode<V>} Returns added child */ this.addChild = function (name, child) { children[name] = child return child } /** * Removes child * * @param {string} name Child name * @return {TreeNode<V>|null} Removed child value */ this.removeChild = function (name) { var value = children[name] delete children[name] return value || null } /** * Fetches current node child by it's name. * * @param {string} name Child name. * * @return {TreeNode.<V>|null} Existing node or nothing. */ this.child = function (name) { return children[name] || null } /** * Sets value for current node. * * @param {V} v * @return {TreeNode} Returns current instance. */ this.set = function (v) { value = v return self } /** * Returns current node value. * * @return {V} */ this.get = function () { return typeof value === 'undefined' ? null : value } /** * Retrieves node at path * * @param {string[]} path * @return {TreeNode<V>|null} */ this.traverse = function (path) { if (path.length === 0) { return this } var name = path.shift() var cursor = this while (name && cursor) { cursor = cursor.child(name) name = path.shift() } return cursor || null } /** * Picks branch as a list of nodes (starting from the root) that match * requested path the most. In the best case, branch would contain root and * all requested nodes, in the worst - branch would consist of root only. * * @param {string[]} path Branch path as list of child names. * @return {[TreeNode<V>]} Picked branch. */ this.branch = function (path) { var branch = [self] var cursor = self var name = path.shift() while (name) { cursor = cursor.child(name) if (!cursor) { break } branch.push(cursor) name = path.shift() } return branch } /** * Puts provided value at designated path down the tree. * * @param {string[]} path Path at which value has to be set. * @param {V} value Value to be set. * @return {TreeNode<V>} Created or updated node. */ this.put = function (path, value) { var cursor = self var name = path.shift() while (name) { if (!cursor.child(name)) { cursor.addChild(name, new TreeNode(null)) } cursor = cursor.child(name) name = path.shift() } return cursor.set(value) } /** * Fetches value closest to provided path. If node contains falsey value, it * is skipped. * * @param {string[]} path Path to pick the branch. * @return {V|null} Target value or null */ this.retrieve = function (path) { return self.branch(path).reverse().reduce(function (carrier, node) { return carrier || node.get() }, null) } /** * Removes node at path and returns it's value * @param {string[]} path * @return {V|undefined} */ this.remove = function (path) { if (path.length === 0) { throw new Error('You can\'t remove root node') } var segment = path.pop() var node = this.traverse(path) var child = node ? node.removeChild(segment) : null return child ? child.get() : null } } /** * This class acts as a structured closure in which logger may operate. * * It encloses logger settings - currently writer and threshold - for every * specified package, so loggers refer to context they were associated in to * find their settings, and end user may configure any logger he wants without * knowing about those loggers (unless particular level has been associated * with particular logger - in that case end user has to override it directly). * * @class * @template T * @implements ILoggerContext.<T> * * @param {Level} [lvl] Root threshold, {@see Level.Info} by default * @param {IWritable} [writer] */ function Context (lvl, writer) { var self = this var writers var levels function reset (lvl, writer) { writers = new TreeNode(writer) levels = new TreeNode(Level.find(lvl) || Level.Info) } reset(lvl, writer) this.reset = reset function path (name) { return (name || '').toString().split('.').filter(function (_) { return _ }) } this.path = path /** * Retrieves level for provided logger * * @param {string} name * @return {Level} */ this.getLevel = function (name) { return levels.retrieve(path(name)) } /** * Retrieves level for provided logger * * @deprecated * * @param {string} name * @return {Level} */ this.getThreshold = this.getLevel /** * Sets level for specified logger * * @param {string} [name] * @param {Level} lvl */ this.setLevel = function (name, lvl) { if (!lvl) { lvl = name name = null } levels.put(path(name), Level.find(lvl)) return self } /** * Sets level for specified logger * * @deprecated * * @param {string} [name] * @param {Level} level */ this.setThreshold = this.setLevel /** * Removes and returns level at provided path * * @param {string} name * @return {Level|undefined} */ this.removeLevel = function (name) { var segments = path(name) if (segments.length === 0) { throw new Error('You can\'t remove default writer') } return levels.remove(segments) } /** * @param {string} name * * @return {IWritable} */ this.getWriter = function (name) { return writers.retrieve(path(name)) } /** * @param {string} [name] Logger name * @param {IWritable} writer */ this.setWriter = function (name, writer) { if (!writer) { writer = name name = null } writers.put(path(name), writer) return self } /** * Removes and returns writer under provided path * * @throws If attempted to remove root writer * * @param {string} name * @return {IWritable|undefined} */ this.removeWriter = function (name) { var segments = path(name) if (segments.length === 0) { throw new Error('You can\'t remove default writer') } return writers.remove(segments) } /** * @abstract * @function Context#create * @param {string} name * @param {Level} [threshold] * @param {IWritable} [writer] * @return {T} */ } /** * @namespace */ module.exports = { Level: Level, Context: Context, TreeNode: TreeNode }