The base path is now *outside* of the macro directory; allowing for a more integrated view of `dfi`'s entire `root` system. This is more apparent once running an interactive session where the expectation is (more intuitively) that any path should be relative to 'src/root' and not 'src/root/macro'. The rationale for why this was in 'src/root/macro' to begin with stems from how `root` (by default) will automatically load rootlogon.C in the directory that `root` is started. This is causing more confusion than not because `dfi`'s usage of `root` is not limited to macros. These changes skirt the line between needing a major API bump and not but, so far, appears to be on the side of *not*. However, the TODOs noted for macro loading should be addressed prior to any API changes.
253 lines
6.8 KiB
Bash
253 lines
6.8 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# docker-finance | modern accounting for the power-user
|
|
#
|
|
# Copyright (C) 2025 Aaron Fiore (Founder, Evergreen Crypto LLC)
|
|
#
|
|
# 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.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
#
|
|
# "Libraries"
|
|
#
|
|
|
|
[ -z "$DOCKER_FINANCE_CONTAINER_REPO" ] && exit 1
|
|
source "${DOCKER_FINANCE_CONTAINER_REPO}/src/finance/lib/internal/lib_utils.bash" || exit 1
|
|
|
|
#
|
|
# Facade
|
|
#
|
|
|
|
function lib_root::root()
|
|
{
|
|
lib_root::__parse_root "$@"
|
|
lib_root::__root "$@"
|
|
lib_utils::catch $?
|
|
}
|
|
|
|
#
|
|
# Implementation
|
|
#
|
|
|
|
function lib_root::__parse_root()
|
|
{
|
|
[ -z "$global_usage" ] && lib_utils::die_fatal
|
|
|
|
local -r _arg="$1"
|
|
local -r _usage="
|
|
\e[32mDescription:\e[0m
|
|
|
|
docker-finance ROOT.cern interpreter
|
|
|
|
\e[32mUsage:\e[0m
|
|
|
|
$ $global_usage [help | [TAB COMPLETION]] [args]
|
|
|
|
\e[32mArguments:\e[0m
|
|
|
|
None: start interactive interpreter w/out running any given macro or plugins
|
|
|
|
[TAB COMPLETION]: load (and run) given macro/plugin when starting interpreter
|
|
|
|
macro = repository macros in repository location
|
|
plugins/custom = custom plugins in custom plugin location
|
|
plugins/repo = repository plugins in repository location
|
|
|
|
[args]: arguments to macro or plugin
|
|
|
|
\e[32mExamples:\e[0m
|
|
|
|
\e[37;2m# See this usage help\e[0m
|
|
$ $global_usage help
|
|
|
|
\e[37;2m# The output of tab completion\e[0m
|
|
$ $global_usage \\\t\\\t
|
|
help macro/crypto/random.C macro/test/unit.C plugins/custom/example.cc
|
|
macro/crypto/hash.C macro/test/benchmark.C macro/web/server.C plugins/repo/example.cc
|
|
|
|
\e[37;2m# Load and run the repo's Web server macro\e[0m
|
|
\e[37;2m# NOTE: this macro will maintain a running ROOT instance\e[0m
|
|
$ $global_usage macro/web/server.C
|
|
|
|
\e[37;2m# Load and run the repo's Hash macro with an argument\e[0m
|
|
\e[37;2m# NOTE: this macro will 'one-off' the ROOT instance\e[0m
|
|
$ $global_usage macro/crypto/hash.C 'this is a test message'
|
|
|
|
\e[37;2m# Load repo example plugin, run within instance\e[0m
|
|
$ $global_usage plugins/repo/example.cc
|
|
root [0]
|
|
Interpreting: '.L /home/business/docker-finance/plugins/root/example.cc'
|
|
root [1] dfi::plugin::my_plugin_namespace::example\\\t
|
|
example1
|
|
example2
|
|
example3
|
|
root [1] dfi::plugin::my_plugin_namespace::example2()
|
|
...
|
|
"
|
|
|
|
if [[ "$#" -gt 0 && ! "$_arg" =~ (^macro/|^plugins/) ]]; then
|
|
lib_utils::die_usage "$_usage"
|
|
fi
|
|
|
|
# Type determined by path stub
|
|
local -r _stub="${_arg#*/}"
|
|
|
|
#
|
|
# Macro
|
|
#
|
|
|
|
if [[ "$_arg" =~ ^macro ]]; then
|
|
# TODO: utilize stub when 'repo/' and 'custom/' is implemented (like plugins)
|
|
local -r _macro="$_arg"
|
|
|
|
# Repo macros
|
|
[ ! -f "${DOCKER_FINANCE_CONTAINER_REPO}/src/root/${_macro}" ] \
|
|
&& lib_utils::die_fatal "repo macro not found '${_macro}'"
|
|
|
|
# TODO: currently, only repo macros are supported
|
|
# (no custom macros, use custom plugins instead)
|
|
|
|
# Per API (v1), requires path stub format
|
|
declare -gr global_arg_macro="$_macro"
|
|
fi
|
|
|
|
#
|
|
# Plugin
|
|
#
|
|
|
|
if [[ "$_arg" =~ ^plugins ]]; then
|
|
local -r _plugin="${_stub#*/}"
|
|
|
|
# Repo plugins
|
|
if [[ "$_stub" =~ ^repo ]]; then
|
|
[ ! -f "${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/${_plugin}" ] \
|
|
&& lib_utils::die_fatal "repo plugin not found '${_plugin}'"
|
|
fi
|
|
|
|
# Custom plugins
|
|
if [[ "$_stub" =~ ^custom ]]; then
|
|
[ -z "$DOCKER_FINANCE_CONTAINER_PLUGINS" ] && lib_utils::die_fatal
|
|
[ ! -f "${DOCKER_FINANCE_CONTAINER_PLUGINS}/root/${_plugin}" ] \
|
|
&& lib_utils::die_fatal "custom plugin not found '${_plugin}'"
|
|
fi
|
|
|
|
# Per API (v1), requires path stub format
|
|
declare -gr global_arg_plugin="$_stub"
|
|
fi
|
|
}
|
|
|
|
function lib_root::__root()
|
|
{
|
|
lib_utils::deps_check "root"
|
|
|
|
local -r _path="${DOCKER_FINANCE_CONTAINER_REPO}/src/root"
|
|
pushd "$_path" 1>/dev/null || lib_utils::die_fatal "could not pushd '${_path}'"
|
|
|
|
#
|
|
# Base command
|
|
#
|
|
|
|
# NOTE:
|
|
#
|
|
# rootlogon.C *MUST* be loaded at any startup; as if fulfills loading
|
|
# of `dfi` source files and other library requirements.
|
|
#
|
|
# Typically, ROOT.cern would load rootlogon.C if we start `root`
|
|
# interactively within the macro directory. However, `dfi` has its own
|
|
# requirements.
|
|
#
|
|
# After rootlogon, any loading of macros, plugins or any `dfi` files
|
|
# can be completed using the `dfi` API.
|
|
|
|
local _exec=("root" "-l" "macro/rootlogon.C")
|
|
|
|
#
|
|
# Interactive opts
|
|
#
|
|
|
|
# Start interactive instance w/out loading/running macro or plugin
|
|
if [[ -z "$global_arg_macro" && -z "$global_arg_plugin" ]]; then
|
|
_exec+=("-e" "help()")
|
|
fi
|
|
|
|
#
|
|
# Macro: non-interactive opts
|
|
#
|
|
|
|
if [ ! -z "$global_arg_macro" ]; then
|
|
_exec+=("-e" "dfi::macro::load(\"${global_arg_macro}\")")
|
|
|
|
# NOTE:
|
|
#
|
|
# - Currently, only repo macros are supported
|
|
#
|
|
# * Per API (v1):
|
|
#
|
|
# - filename is named after class
|
|
# * classes begin uppercase
|
|
# * filename begins lowercase
|
|
#
|
|
# - run() static function is the entry point for all macros
|
|
#
|
|
|
|
# TODO: modify substring when 'repo/' and 'custom/' is implemented (like plugins)
|
|
|
|
local _namespace="${global_arg_macro#*/}"
|
|
_namespace="${_namespace%/*}"
|
|
declare -r _namespace
|
|
|
|
local _class="${global_arg_macro##*/}"
|
|
_class="${_class^}"
|
|
_class="${_class%.*}"
|
|
declare -r _class
|
|
|
|
local -r _caller="dfi::macro::${_namespace}::${_class}"
|
|
|
|
# Per-API signatures (v1)
|
|
if [[ ${_class} == "Hash" ]]; then
|
|
# If no message is given, default to unique profile/subprofile
|
|
[[ -z "$global_parent_profile" || -z "$global_arg_delim_1" || -z "$global_child_profile" ]] && lib_utils::die_fatal
|
|
[ -z "${*:2}" ] \
|
|
&& _run="run(\"${global_parent_profile}${global_arg_delim_1}${global_child_profile}\")" \
|
|
|| _run="run(\"${*:2}\")"
|
|
else
|
|
_run="run()"
|
|
fi
|
|
|
|
_exec+=("-e" "${_caller}::${_run}")
|
|
|
|
# NOTE: when called via shell, server(s) are implied to
|
|
# require an interactive ROOT instance (not a 'one-off').
|
|
if [[ "${_class%.*}" != "Server" ]]; then
|
|
_exec+=('-q')
|
|
fi
|
|
fi
|
|
|
|
#
|
|
# Plugin: non-interactive opts
|
|
#
|
|
|
|
if [ ! -z "$global_arg_plugin" ]; then
|
|
_exec+=("-e" "dfi::plugin::load(\"${global_arg_plugin}\")")
|
|
fi
|
|
|
|
#
|
|
# Execute
|
|
#
|
|
|
|
lib_utils::print_debug "${_exec[@]}"
|
|
"${_exec[@]}" || lib_utils::die_fatal "root exited with: $?"
|
|
}
|
|
|
|
# vim: sw=2 sts=2 si ai et
|