#const ROOIBOS_ERROR_LOGS = true
#const ROOIBOS_WARNING_LOGS = false
#const ROOIBOS_INFO_LOGS = true
#const ROOIBOS_DEBUG_LOGS = false
#const ROOIBOS_TRACE_LOGS = false
namespace rooibos.common
' ******************
' Common utility functions
' ******************
' check if value contains XMLElement interface
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value contains XMLElement interface, else return false
function isXmlElement(value as dynamic) as boolean
return rooibos.common.isValid(value) and GetInterface(value, "ifXMLElement") <> invalid
end function
' check if value contains Function interface
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value contains Function interface, else return false
function isFunction(value as dynamic) as boolean
return rooibos.common.isValid(value) and GetInterface(value, "ifFunction") <> invalid
end function
' looks up the function by name, for the function map
' @param {String} filename - name of the file where the function was found
' @param {String} functionName - name of the function to locate
' @returns {Function} function pointer or invalid
function getFunction(filename as dynamic, functionName as dynamic) as object
if not rooibos.common.isNotEmptyString(functionName) or not rooibos.common.isNotEmptyString(filename) then
return invalid
end if
mapFunction = RBSFM_getFunctionsForFile(filename) 'bs:disable-line 1140 LINT1001
if mapFunction <> invalid then
map = mapFunction()
if type(map) = "roAssociativeArray" then
functionPointer = map[functionName]
return functionPointer
else
return invalid
end if
end if
return invalid
end function
' looks up the function by name, from any function map in future
' @param {String} functionName - name of the function to locate
' @returns {Function} function pointer or invalid
function getFunctionBruteForce(functionName as dynamic) as object
if not rooibos.common.isNotEmptyString(functionName) then
return invalid
end if
filenames = RBSFM_getFilenames() 'bs:disable-line 1140 LINT1001
for i = 0 to filenames.count() - 1
filename = filenames[i]
mapFunction = RBSFM_getFunctionsForFile(filename) 'bs:disable-line 1140 LINT1001
if mapFunction <> invalid then
map = mapFunction()
if type(map) = "roAssociativeArray" then
functionPointer = map[functionName]
if functionPointer <> invalid then
return functionPointer
end if
end if
end if
end for
return invalid
end function
' check if value contains Boolean interface
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value contains Boolean interface, else return false
function isBoolean(value as dynamic) as boolean
return rooibos.common.isValid(value) and GetInterface(value, "ifBoolean") <> invalid
end function
' check if value type equals Integer
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value type equals Integer, else return false
function isInteger(value as dynamic) as boolean
return rooibos.common.isValid(value) and GetInterface(value, "ifInt") <> invalid and (Type(value) = "roInt" or Type(value) = "roInteger" or Type(value) = "Integer")
end function
' check if value contains Float interface
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value contains Float interface, else return false
function isFloat(value as dynamic) as boolean
return rooibos.common.isValid(value) and GetInterface(value, "ifFloat") <> invalid
end function
' check if value contains Double interface
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value contains Double interface, else return false
function isDouble(value as dynamic) as boolean
return rooibos.common.isValid(value) and GetInterface(value, "ifDouble") <> invalid
end function
' check if value contains LongInteger interface
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value contains LongInteger interface, else return false
function isLongInteger(value as dynamic) as boolean
return rooibos.common.isValid(value) and GetInterface(value, "ifLongInt") <> invalid
end function
' check if value contains LongInteger or Integer or Double or Float interface
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value is number, else return false
function isNumber(value as dynamic) as boolean
return rooibos.common.isLongInteger(value) or rooibos.common.isDouble(value) or rooibos.common.isInteger(value) or rooibos.common.isFloat(value)
end function
' check if value contains List interface
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value contains List interface, else return false
function isList(value as dynamic) as boolean
return rooibos.common.isValid(value) and GetInterface(value, "ifList") <> invalid
end function
' check if value contains Array interface
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value contains Array interface, else return false
function isArray(value as dynamic) as boolean
return rooibos.common.isValid(value) and GetInterface(value, "ifArray") <> invalid
end function
' check if value contains AssociativeArray interface
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value contains AssociativeArray interface, else return false
function isAssociativeArray(value as dynamic) as boolean
return rooibos.common.isValid(value) and GetInterface(value, "ifAssociativeArray") <> invalid
end function
' check if value contains SGNodeChildren interface
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value contains SGNodeChildren interface, else return false
function isSGNode(value as dynamic) as boolean
return rooibos.common.isValid(value) and GetInterface(value, "ifSGNodeChildren") <> invalid
end function
' check if value contains String interface
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value contains String interface, else return false
function isString(value as dynamic) as boolean
return rooibos.common.isValid(value) and GetInterface(value, "ifString") <> invalid
end function
' check if value contains String interface and length more 0
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value contains String interface and length more 0, else return false
function isNotEmptyString(value as dynamic) as boolean
return rooibos.common.isString(value) and len(value) > 0
end function
' check if value contains DateTime interface
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value contains DateTime interface, else return false
function isDateTime(value as dynamic) as boolean
return rooibos.common.isValid(value) and (GetInterface(value, "ifDateTime") <> invalid or Type(value) = "roDateTime")
end function
' check if value initialized and not equal invalid
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value initialized and not equal invalid, else return false
function isValid(value as dynamic) as boolean
return not rooibos.common.isUndefined(value) and value <> invalid
end function
' check if value uninitialized or empty string
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value is uninitialized or "", else return false
function isUndefined(value as dynamic) as boolean
return type(value) = "" or Type(value) = "<uninitialized>"
end function
' return value if his contains String interface else return empty string
' @param {Dynamic} value - value to check
' @returns {String} value if his contains String interface else return empty string
function validStr(value as dynamic) as string
if value <> invalid and GetInterface(value, "ifString") <> invalid then
return value
else
return ""
end if
end function
' /**
' convert value to multiline String if this possible, else return empty string
' @param {Dynamic} value - value to check
' @returns {String} converted string
function asMultilineString(value as dynamic, includeType = false as boolean, indention = 0 as integer) as string
indentChr = " "
if rooibos.common.isValid(value) = false then
return type(value)
else if rooibos.common.isString(value) then
return formatJson(value)
else if rooibos.common.isInteger(value) or rooibos.common.isLongInteger(value) or rooibos.common.isBoolean(value) then
if includeType then
return value.ToStr() + " (" + rooibos.common.getSafeType(value) + ")"
else
return value.ToStr()
end if
else if rooibos.common.isFloat(value) or rooibos.common.isDouble(value) then
if includeType then
return Str(value).Trim() + " (" + rooibos.common.getSafeType(value) + ")"
else
return Str(value).Trim()
end if
else if type(value) = "roSGNode" then
return "Node(" + value.subType() + ")"
else if type(value) = "roAssociativeArray" then
if value.isEmpty() then
return "{" + chr(10) + string(indention, indentChr) + "}"
end if
text = "{" + chr(10)
keys = value.ifAssociativeArray.keys()
keys.sort()
for each key in keys
if rooibos.common.canSafelyIterateAAKey(value, key) then
text = text + string(indention + 1, indentChr) + formatJson(key) + ": " + rooibos.common.asMultilineString(value[key], includeType, indention + 1) + "," + chr(10)
end if
end for
' remove last comma
if len(text) > 2 then
text = left(text, len(text) - 2)
end if
text = text + chr(10) + string(indention, indentChr) + "}"
return text
else if rooibos.common.isArray(value) then
if value.isEmpty() then
return "[" + chr(10) + "]"
end if
text = "[" + chr(10)
for i = 0 to value.count() - 1
v = value[i]
text += string(indention + 1, indentChr) + rooibos.common.asMultilineString(v, includeType, indention + 1)
if i < value.count() - 1 then
text += ","
end if
text += chr(10)
end for
text = text + string(indention, indentChr) + "]"
return text
else if rooibos.common.isFunction(value) then
return value.toStr().mid(10) + " (function)"
else
return ""
end if
end function
' convert value to String if this possible, else return empty string
' @param {Dynamic} value - value to check
' @returns {String} converted string
function asString(value as dynamic, includeType = false as boolean) as string
if rooibos.common.isValid(value) = false then
return "INVALID"
else if rooibos.common.isString(value) then
if includeType then
return """" + value + """"
else
return value
end if
else if rooibos.common.isInteger(value) or rooibos.common.isLongInteger(value) or rooibos.common.isBoolean(value) then
if includeType then
return value.ToStr() + " (" + rooibos.common.getSafeType(value) + ")"
else
return value.ToStr()
end if
else if rooibos.common.isFloat(value) or rooibos.common.isDouble(value) then
if includeType then
return Str(value).Trim() + " (" + rooibos.common.getSafeType(value) + ")"
else
return Str(value).Trim()
end if
else if type(value) = "roSGNode" then
return "Node(" + value.subType() + ")"
else if type(value) = "roAssociativeArray" then
isFirst = true
text = "{"
if not isFirst then
text = text + ","
isFirst = false 'bs:disable-line: LINT1005
end if
keys = value.ifAssociativeArray.keys()
keys.sort()
for each key in keys
if rooibos.common.canSafelyIterateAAKey(value, key) then
text = text + key + ":" + rooibos.common.asString(value[key], includeType)
end if
end for
text = text + "}"
return text
else if rooibos.common.isArray(value) then
text = "["
join = ""
maxLen = 500
for each v in value
if len(text) < maxLen then
text += join + rooibos.common.asString(v, includeType)
join = ", "
end if
end for
if len(text) > maxLen then
text = left(text, maxLen - 3) + "..."
end if
text = text + "]"
return text
else if rooibos.common.isFunction(value) then
return value.toStr() + "(function)"
else
return ""
end if
end function
' convert value to Integer if this possible, else return 0
' @param {Dynamic} value - value to check
' @returns {Integer} converted Integer
function asInteger(value as dynamic) as integer
if rooibos.common.isValid(value) = false then
return 0
else if rooibos.common.isString(value) then
return value.ToInt()
else if rooibos.common.isInteger(value) then
return value
else if rooibos.common.isFloat(value) or rooibos.common.isDouble(value) or rooibos.common.isLongInteger(value) then
return Int(value)
else
return 0
end if
end function
' convert value to LongInteger if this possible, else return 0
' @param {Dynamic} value - value to check
' @returns {Integer} converted LongInteger
function asLongInteger(value as dynamic) as longinteger
if rooibos.common.isValid(value) = false then
return 0
else if rooibos.common.isString(value) then
return rooibos.common.asInteger(value)
else if rooibos.common.isLongInteger(value) or rooibos.common.isFloat(value) or rooibos.common.isDouble(value) or rooibos.common.isInteger(value) then
return value
else
return 0
end if
end function
' convert value to Float if this possible, else return 0.0
' @param {Dynamic} value - value to check
' @returns {Float} converted Float
function asFloat(value as dynamic) as float
if rooibos.common.isValid(value) = false then
return 0.0
else if rooibos.common.isString(value) then
return value.ToFloat()
else if rooibos.common.isInteger(value) then
return (value / 1)
else if rooibos.common.isFloat(value) or rooibos.common.isDouble(value) or rooibos.common.isLongInteger(value) then
return value
else
return 0.0
end if
end function
' convert value to Double if this possible, else return 0.0
' @param {Dynamic} value - value to check
' @returns {Float} converted Double
function asDouble(value as dynamic) as double
if rooibos.common.isValid(value) = false then
return 0.0
else if rooibos.common.isString(value) then
return rooibos.common.asFloat(value)
else if rooibos.common.isInteger(value) or rooibos.common.isLongInteger(value) or rooibos.common.isFloat(value) or rooibos.common.isDouble(value) then
return value
else
return 0.0
end if
end function
' convert value to Boolean if this possible, else return False
' @param {Dynamic} value - value to check
' @returns {Boolean} converted boolean
function asBoolean(value as dynamic) as boolean
if rooibos.common.isValid(value) = false then
return false
else if rooibos.common.isString(value) then
return lCase(value) = "true"
else if rooibos.common.isInteger(value) or rooibos.common.isFloat(value) then
return value <> 0
else if rooibos.common.isBoolean(value) then
return value
else
return false
end if
end function
' if type of value equals array return value, else return array with one element [value]
' @param {Dynamic} value - value to check
' @returns {roArray} converted array
function asArray(value as dynamic) as object
if rooibos.common.isValid(value) then
if not rooibos.common.isArray(value) then
return [value]
else
return value
end if
end if
return []
end function
'=====================
' Strings
'=====================
' check if value is invalid or empty
' @param {Dynamic} value - value to check
' @returns {Boolean} true if value is null or empty string, else return false
function isNullOrEmpty(value as dynamic) as boolean
if rooibos.common.isString(value) then
return Len(value) = 0
else
return not rooibos.common.isValid(value)
end if
end function
'=====================
' Arrays
'=====================
' find an element index in array
' @param {Dynamic} array - array to search
' @param {Dynamic} value - value to check
' @param {Dynamic} compareAttribute - attribute to use for comparisons
' @param {Boolean} caseSensitive - indicates if comparisons are case sensitive
' @returns {Integer} element index if array contains a value, else return -1
function findElementIndexInArray(array as dynamic, value as dynamic, compareAttribute = invalid as dynamic, caseSensitive = false as boolean, callCount = 0 as integer) as integer
if callCount = 0 and not rooibos.common.isArray(array) then
array = rooibos.common.asArray(array)
end if
if rooibos.common.isArray(array) then
for i = 0 to rooibos.common.asArray(array).Count() - 1
compareValue = array[i]
if compareAttribute <> invalid and rooibos.common.isAssociativeArray(compareValue) then
compareValue = compareValue.ifAssociativeArray.lookupCI(compareAttribute)
end if
if rooibos.common.eqValues(compareValue, value, callCount + 1) then
return i
end if
next
end if
return -1
end function
' check if array contains specified value
' @param {Dynamic} array - array to search in
' @param {Dynamic} value - value to check
' @param {Dynamic} compareAttribute - attribute to compare on
' @returns {Boolean} true if array contains a value, else return false
function arrayContains(array as dynamic, value as dynamic, compareAttribute = invalid as dynamic) as boolean
return rooibos.common.findElementIndexInArray(array, value, compareAttribute) > -1
end function
'=====================
' NODES
'=====================
' find an element index in node
' @param {Dynamic} node - node to search in
' @param {Dynamic} value - child to search for
' @returns {Integer} element index if node contains a value, else return -1
function findElementIndexInNode(node as dynamic, value as dynamic) as integer
if type(node) = "roSGNode" then
if node.isSubType("mc_Node") then
for i = 0 to node.length - 1
compareValue = node@.getChild(i)
if type(compareValue) = "roSGNode" and compareValue.isSameNode(value) then
return i
end if
end for
else
for i = 0 to node.getChildCount() - 1
compareValue = node.getChild(i)
if type(compareValue) = "roSGNode" and compareValue.isSameNode(value) then
return i
end if
end for
end if
end if
return -1
end function
' check if node contains specified child
' @param {Dynamic} node - the node to check on
' @param {Dynamic} value - child to look for
' @returns {Boolean} true if node contains a value, else return false
function nodeContains(node as dynamic, value as dynamic) as boolean
return rooibos.common.findElementIndexInNode(node, value) > -1
end function
function getSafeType(v as dynamic) as string or dynamic
t = type(v)
if t = "" then
return invalid
else if t = "<uninitialized>" then
return "<uninitialized>"
else if t = "roString" then
return "String"
else if t = "roInteger" then
return "Integer"
else if t = "roBoolean" then
return "Boolean"
else if t = "roBool" then
return "Boolean"
else if t = "roInt" then
return "Integer"
else if t = "roList" then
return "List"
else if t = "roFloat" then
return "Float"
else if t = "roDouble" then
return "Double"
else if t = "roInvalid" then
return "Invalid"
else
return t
end if
end function
' Takes a value and if the value is not a primitive it will wrap the type in a Component: tag like the debugger does
' @param {Dynamic} value - value to check the type of
' @param {Boolean} includeSubtype - If true and the value is a node the result will include the node subtype
' @returns {String} Formatted result. Examples: 'String', 'Integer', '<Component: roDateTime>', '<Component: roSGNode:Node>'
function getTypeWithComponentWrapper(value as dynamic, includeSubtype = false as boolean) as string
if not rooibos.common.isValid(value) or rooibos.common.isNumber(value) or rooibos.common.isString(value) or rooibos.common.isBoolean(value) then
return type(value)
else
if includeSubtype and rooibos.common.isSGNode(value) then
return `<Component: ${type(value)}:${value.subType()}>`
else
return `<Component: ${type(value)}>`
end if
end if
end function
' Takes a string and formats and truncates a string for more compact printing.
' @param {Dynamic} value - string to format
' @param {Integer} maxLength - the max length of the resulting string
' @param {Boolean} collapseNewlines - Will convert newlines and spaces into a single space
' @returns {String} Formatted result
function truncateString(value as string, length = 38 as integer, collapseNewlines = true as boolean) as string
if collapseNewlines then
value = CreateObject("roRegex", "\n\s*", "g").replaceAll(value, " ")
end if
if len(value) > length then
value = value.mid(0, length - 1) + "…"
end if
return value
end function
' Compare two arbitrary values to each-other.
' @param {Dynamic} Value1 - first item to compare
' @param {Dynamic} Value2 - second item to compare
' @returns {Boolean} True if values are equal or False in other case.
function eqValues(Value1 as dynamic, Value2 as dynamic, fuzzy = false as boolean, callCount = 0 as integer) as dynamic
if callCount > 10 then
rooibos.common.logError("REACHED MAX ITERATIONS DOING COMPARISON")
return true
end if
' Workaround for bug with string boxing, and box everything else
val1Type = rooibos.common.getSafeType(Value1)
val2Type = rooibos.common.getSafeType(Value2)
if val1Type = invalid or val2Type = invalid then
rooibos.common.logError("undefined value passed")
return false
end if
'Upcast int to float, if other is float
if val1Type = "Float" and val2Type = "Integer" then
Value2 = cDbl(Value2)
else if val2Type = "Float" and val1Type = "Integer" then
Value1 = cDbl(Value1)
end if
if val1Type <> val2Type and (fuzzy <> true or val1Type = "String" or val2Type = "String") then
return false
else
valType = val1Type
if val1Type = "List" then
return rooibos.common.eqArray(Value1, Value2, fuzzy, callCount + 1)
else if valType = "roAssociativeArray" then
return rooibos.common.eqAssocArray(Value1, Value2, fuzzy, callCount + 1)
else if valType = "roArray" then
return rooibos.common.eqArray(Value1, Value2, fuzzy, callCount + 1)
else if valType = "roSGNode" then
if val2Type <> "roSGNode" then
return false
else
return Value1.isSameNode(Value2)
end if
else if valType = "<uninitialized>" and val2Type = "<uninitialized>" then
' Both values are uninitialized, so they are equal
return true
else if valType = "<uninitialized>" or val2Type = "<uninitialized>" then
' One value is uninitialized, so they are not equal due to passing previous check
return false
else
if fuzzy = true then
return rooibos.common.asString(Value1) = rooibos.common.asString(Value2)
else
'If you crashed on this line, then you're trying to compare
'2 things which can't be compared - check what value1 and value2
'are in your debug log
return Value1 = Value2
end if
end if
end if
end function
function eqTypes(Value1 as dynamic, Value2 as dynamic) as dynamic
val1Type = rooibos.common.getSafeType(Value1)
val2Type = rooibos.common.getSafeType(Value2)
if val1Type = invalid or val2Type = invalid then
' TODO: this doesn't actually feel like an error, Need to talk about this.
rooibos.common.logError("undefined value passed")
return false
end if
'Upcast int to float, if other is float
if val1Type = "Float" and val2Type = "Integer" then
Value2 = cDbl(Value2)
else if val2Type = "Float" and val1Type = "Integer" then
Value1 = cDbl(Value1)
end if
return val1Type <> val2Type
end function
' Compare to roAssociativeArray objects for equality.
' @param {Dynamic} Value1 - first associative array
' @param {Dynamic} Value2 - second associative array
' @returns {Boolean} True if arrays are equal or False in other case.
function eqAssocArray(Value1 as dynamic, Value2 as dynamic, fuzzy = false as boolean, callCount = 0 as integer) as dynamic
if not rooibos.common.isAssociativeArray(Value1) or not rooibos.common.isAssociativeArray(Value2) then
return false
end if
l1 = Value1.ifAssociativeArray.Count()
l2 = Value2.ifAssociativeArray.Count()
if not l1 = l2 then
return false
else
for each k in Value1
if not Value2.ifAssociativeArray.DoesExist(k) then
return false
else
if rooibos.common.canSafelyIterateAAKey(Value1, k) and rooibos.common.canSafelyIterateAAKey(Value2, k) then
v1 = Value1[k]
v2 = Value2[k]
if not rooibos.common.eqValues(v1, v2, fuzzy, callCount + 1) then
return false
end if
end if
end if
end for
return true
end if
end function
function canSafelyIterateAAKey(aa as roAssociativeArray, key as string) as boolean
if lCase(key) = "__rooibosskipfields" or key = "__mocks" or key = "__stubs" or key = "log" or key = "top" or key = "m" then 'fix infinite loop/box crash when doing equals on an aa with a mock
return false
else if aa.__rooibosSkipFields <> invalid and aa.__rooibosSkipFields.doesExist(key) then
return false
end if
return true
end function
' Compare to roArray objects for equality.
' @param {Dynamic} Value1 - first array
' @param {Dynamic} Value2 - second array
' @returns {Boolean} True if arrays are equal or False in other case.
function eqArray(Value1 as dynamic, Value2 as dynamic, fuzzy = false as boolean, callCount = 0 as integer) as dynamic
if callCount > 30 then
rooibos.common.logError("REACHED MAX ITERATIONS DOING COMPARISON")
return true
end if
if not (rooibos.common.isArray(Value1)) or not rooibos.common.isArray(Value2) then
return false
end if
l1 = Value1.Count()
l2 = Value2.Count()
if not l1 = l2 then
return false
else
for i = 0 to l1 - 1
v1 = Value1[i]
v2 = Value2[i]
if not rooibos.common.eqValues(v1, v2, fuzzy, callCount + 1) then
return false
end if
end for
return true
end if
end function
' Fills text with count of fillChars
' @param {String} text - text to fill
' @param {String} fillChar - char to fill with
' @param {Integer} numChars - target length
' @returns {String} filled string
function fillText(text as string, fillChar = " " as string, numChars = 40 as integer) as string
if len(text) >= numChars then
text = left(text, numChars - 5) + "..." + fillChar + fillChar
else
numToFill = numChars - len(text) - 1
for i = 0 to numToFill
text = text + fillChar
end for
end if
return text
end function
function makePathStubbable(content as dynamic, path as string) as dynamic
part = invalid
if path <> invalid then
parts = path.split(".")
numParts = parts.count()
i = 0
contentName = parts[i]
i++
if type(content) <> "roAssociativeArray" then
content = { id: contentName }
end if
part = content
while i < numParts and part <> invalid
isIndexNumber = parts[i] = "0" or (parts[i].toInt() <> 0 and parts[i].toInt().toStr() = parts[i])
index = invalid
if isIndexNumber then
index = parts[i].toInt()
else
index = parts[i]
end if
nextPart = invalid
if rooibos.common.isArray(part) and isIndexNumber then
nextPart = part[index]
else if type(part) = "roAssociativeArray" and not isIndexNumber then
nextPart = part[index]
end if
if nextPart = invalid or type(nextPart) <> "roAssociativeArray" then
if (not isIndexNumber and type(part) = "roAssociativeArray") or (isIndexNumber and (rooibos.common.isArray(part))) then
nextPart = { id: index }
part[index] = nextPart
else
'index type mismatch, gonna have to bail
return content
end if
end if
part = nextPart
i++
end while
end if
return part
end function
' @ignore
function logError(value as dynamic)
#if ROOIBOS_ERROR_LOGS
? "[Rooibos Error]: " value
#end if
end function
' @ignore
function logWarning(value as dynamic)
#if ROOIBOS_WARNING_LOGS
? "[Rooibos Warning]: " value
#end if
end function
' @ignore
function logInfo(value as dynamic)
#if ROOIBOS_INFO_LOGS
? "[Rooibos Info]: " value
#end if
end function
' @ignore
function logDebug(value as dynamic)
#if ROOIBOS_DEBUG_LOGS
? "[Rooibos Debug]: " value
#end if
end function
' @ignore
function logTrace(value as dynamic)
#if ROOIBOS_TRACE_LOGS
? "[Rooibos Trace]: " value
#end if
end function
end namespace