Merge pull request #215 into master

c68a668 container: finance: completion: add 'help' to `plugins` (Aaron Fiore)
c336451 container: finance: lib_plugins: update usage help (Aaron Fiore)
745a16e container: finance: completion: add `root` (Aaron Fiore)
3348666 container: root: rootlogon.C: remove default help() (Aaron Fiore)
ed16344 container: finance: add lib_root impl (macro/plugin support) (Aaron Fiore)
This commit is contained in:
2025-09-04 15:11:03 -07:00
5 changed files with 277 additions and 15 deletions

View File

@@ -129,13 +129,26 @@ function docker-finance::completion()
find "${DOCKER_FINANCE_CONTAINER_REPO}"/plugins/finance "${_args[@]}" -printf 'repo/%P\n' 2>/dev/null
})
declare -r _plugins
mapfile -t _reply < <(compgen -W "${_plugins[*]}" -- "$_cur")
mapfile -t _reply < <(compgen -W "help ${_plugins[*]}" -- "$_cur")
;;
reports)
mapfile -t _reply < <(compgen -W "help all${global_arg_delim_2} type${global_arg_delim_2} interval${global_arg_delim_2} year${global_arg_delim_2}" -- "$_cur")
;;
root)
# TODO: currently no-op
compopt +o nospace
[ -z "$DOCKER_FINANCE_CONTAINER_REPO" ] && lib_utils::die_fatal
[ -z "$DOCKER_FINANCE_CONTAINER_PLUGINS" ] && lib_utils::die_fatal
local -r _args=("-type" "f" "-not" "-path" "*/internal/*" "-not" "-path" "*/impl/*" "-not" "-path" "*/common/*" "!" "-name" "rootlogon.C")
local _root
mapfile -t _root < <({
find "${DOCKER_FINANCE_CONTAINER_PLUGINS}"/root "${_args[@]}" -printf 'plugins/custom/%P\n' 2>/dev/null
find "${DOCKER_FINANCE_CONTAINER_REPO}"/plugins/root "${_args[@]}" -printf 'plugins/repo/%P\n' 2>/dev/null
find "${DOCKER_FINANCE_CONTAINER_REPO}"/src/root/macro "${_args[@]}" -printf 'macro/%P\n' 2>/dev/null
})
declare -r _root
mapfile -t _reply < <(compgen -W "help ${_root[*]}" -- "$_cur")
;;
taxes)
mapfile -t _reply < <(compgen -W "help all${global_arg_delim_2} tag${global_arg_delim_2} account${global_arg_delim_2} year${global_arg_delim_2} write${global_arg_delim_2}" -- "$_cur")

View File

@@ -2,7 +2,7 @@
# docker-finance | modern accounting for the power-user
#
# Copyright (C) 2024 Aaron Fiore (Founder, Evergreen Crypto LLC)
# Copyright (C) 2024-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
@@ -65,18 +65,39 @@ function lib_plugins::__parse_args()
local -r _usage="
\e[32mDescription:\e[0m
Execute a categorical plugin
Execute a categorical shell plugin (non-\`root\`)
NOTE: for \`root\` plugins, see \`root help\`
\e[32mUsage:\e[0m
$ $global_usage <repo${global_arg_delim_1}plugin | custom${global_arg_delim_1}plugin> [args]
$ $global_usage [help | [TAB COMPLETION]] [args]
\e[32mArguments:\e[0m
[None | help]: show this usage help
[TAB COMPLETION]: run given shell plugin
custom = custom plugins in custom plugin location
repo = repository plugins in repository location
[args]: arguments to plugin
\e[32mExamples:\e[0m
\e[37;2m# Execute a repository plugin in '${_repo}'\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
custom/billing/invoice.bash help repo/timew_to_timeclock.bash
custom/billing/manage.bash repo/example.bash
\e[37;2m# Execute a repository shell plugin in '${_repo}'\e[0m
$ $global_usage repo${global_arg_delim_1}example.bash \"I'm in repo\"
\e[37;2m# Execute a custom plugin in '${_custom}'\e[0m
\e[37;2m# Execute a custom shell plugin in '${_custom}'\e[0m
$ $global_usage custom${global_arg_delim_1}example.bash \"I'm in custom\"
"

View 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

View File

@@ -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 $?
}

View File

@@ -196,9 +196,6 @@ void rootlogon()
gInterpreter->ProcessLine(".L ../src/hash.hh");
gInterpreter->ProcessLine(".L ../src/random.hh");
gInterpreter->ProcessLine(".L ../src/utility.hh");
// Help usage
help();
}
#endif // CONTAINER_SRC_ROOT_MACRO_ROOTLOGON_C_