container: finance: add lib_root impl (macro/plugin support)
- dfi's `root` now supports shell loading (and running) of
macros/plugins:
* An interactive ROOT.cern instance is no longer *always* needed:
- For macros (non-server); a 'one-off' instance will start, load the
given macro, run the macro and then the instance will exit.
* Server(s) will maintain an interactive instance.
- For plugins; an interactive instance will start, load the given
plugin, and then leave the user to make their calls manually.
* All repo macros and repo/custom plugins are available.
- By default, an interactive instance will start when no dfi `root`
arguments are given.
See `root help` for details.
This commit is contained in:
234
container/src/finance/lib/internal/lib_root.bash
Normal file
234
container/src/finance/lib/internal/lib_root.bash
Normal file
@@ -0,0 +1,234 @@
|
||||
#!/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
|
||||
local -r _macro="$_stub"
|
||||
|
||||
# Repo macros
|
||||
[ ! -f "${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/${_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="$_stub"
|
||||
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 _exec=("root" "-l")
|
||||
|
||||
#
|
||||
# Default
|
||||
#
|
||||
|
||||
# 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
|
||||
#
|
||||
|
||||
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
|
||||
#
|
||||
local -r _file="${global_arg_macro##*/}"
|
||||
local -r _stub="${_file^}"
|
||||
|
||||
local -r _class="${_stub%.*}"
|
||||
local -r _caller="dfi::macro::${global_arg_macro%%/*}::${_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
|
||||
#
|
||||
|
||||
if [ ! -z "$global_arg_plugin" ]; then
|
||||
_exec+=("-e" "dfi::plugin::load(\"${global_arg_plugin}\")")
|
||||
fi
|
||||
|
||||
#
|
||||
# Execute
|
||||
#
|
||||
|
||||
# NOTE:
|
||||
#
|
||||
# This *MUST* be executed from within the macro directory,
|
||||
# regardless of what is loaded (macro or plugin), as ROOT.cern
|
||||
# will automatically load rootlogon.C (which `dfi` needs).
|
||||
#
|
||||
# Any loading or macros of plugins from within an interactive
|
||||
# instance can use be completed with the `dfi` API.
|
||||
#
|
||||
lib_utils::print_debug "${_exec[@]}"
|
||||
pushd "${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro" 1>/dev/null \
|
||||
&& "${_exec[@]}" \
|
||||
|| lib_utils::die_fatal "could not pushd"
|
||||
}
|
||||
|
||||
# vim: sw=2 sts=2 si ai et
|
||||
@@ -90,6 +90,7 @@ function lib_finance::finance()
|
||||
source "${DOCKER_FINANCE_CONTAINER_REPO}/src/finance/lib/internal/lib_meta.bash" || exit 1
|
||||
source "${DOCKER_FINANCE_CONTAINER_REPO}/src/finance/lib/internal/lib_plugins.bash" || exit 1
|
||||
source "${DOCKER_FINANCE_CONTAINER_REPO}/src/finance/lib/internal/lib_reports.bash" || exit 1
|
||||
source "${DOCKER_FINANCE_CONTAINER_REPO}/src/finance/lib/internal/lib_root.bash" || exit 1
|
||||
source "${DOCKER_FINANCE_CONTAINER_REPO}/src/finance/lib/internal/lib_taxes.bash" || exit 1
|
||||
source "${DOCKER_FINANCE_CONTAINER_REPO}/src/finance/lib/internal/lib_times.bash" || exit 1
|
||||
}
|
||||
@@ -216,11 +217,7 @@ function lib_finance::reports()
|
||||
|
||||
function lib_finance::root()
|
||||
{
|
||||
# TODO: lib_root, add extendable arguments
|
||||
lib_utils::deps_check "root"
|
||||
# NOTE: will automatically load rootlogon.C
|
||||
pushd "${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro" 1>/dev/null \
|
||||
&& root -l
|
||||
lib_root::root "$@"
|
||||
lib_utils::catch $?
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user