Files
docker-finance/container/src/finance/lib/internal/lib_root.bash
Aaron Fiore 9fec1b427c container: root: change base path, related refactor
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.
2025-11-21 17:32:25 -08:00

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