#!/usr/bin/env bash # docker-finance | modern accounting for the power-user # # Copyright (C) 2021-2024 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 . # # "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_edit::edit() { lib_edit::__parse_args "$@" lib_edit::__edit lib_utils::catch $? } # # Implementation # function lib_edit::__parse_args() { [ -z "$global_usage" ] && lib_utils::die_fatal [ -z "$global_arg_delim_1" ] && lib_utils::die_fatal [ -z "$global_arg_delim_2" ] && lib_utils::die_fatal [ -z "$global_arg_delim_3" ] && lib_utils::die_fatal local -r _usage=" \e[32mDescription:\e[0m Edit container configuration files and journals \e[32mUsage:\e[0m $ $global_usage > [account${global_arg_delim_2}] \e[32mArguments:\e[0m Configuration type: type${global_arg_delim_2} Account: account${global_arg_delim_2} \e[32mExamples:\e[0m \e[37;2m# Edit fetch configuration\e[0m $ $global_usage type${global_arg_delim_2}fetch \e[37;2m# Edit meta and subprofile configurations\e[0m $ $global_usage type${global_arg_delim_2}meta${global_arg_delim_3}shell \e[37;2m# Edit _manual_ journal for year 2023\e[0m $ $global_usage type${global_arg_delim_2}manual year=2023 \e[37;2m# Edit subaccount rules for acccount${global_arg_delim_1}subaccount\e[0m $ $global_usage type${global_arg_delim_2}rules account${global_arg_delim_2}gemini${global_arg_delim_1}exchange \e[37;2m# Edit shared rules for multiple accounts\e[0m $ $global_usage type${global_arg_delim_2}rules account${global_arg_delim_2}electrum${global_arg_delim_3}ethereum-based \e[37;2m# Edit subaccount rules for multiple account${global_arg_delim_1}subaccount\e[0m $ $global_usage account${global_arg_delim_2}ledger${global_arg_delim_1}nano${global_arg_delim_3}trezor${global_arg_delim_1}model type${global_arg_delim_2}rules \e[37;2m# Edit subaccount preprocess and rules for multiple account${global_arg_delim_1}subaccount\e[0m $ $global_usage account${global_arg_delim_2}coinbase${global_arg_delim_1}platform${global_arg_delim_3}electrum${global_arg_delim_1}wallet-1${global_arg_delim_3}coinomi${global_arg_delim_1}wallet-1 type${global_arg_delim_2}preprocess${global_arg_delim_3}rules " # # Ensure supported arguments # [ $# -eq 0 ] && lib_utils::die_usage "$_usage" for _arg in "$@"; do [[ ! "$_arg" =~ ^type[s]?${global_arg_delim_2} ]] \ && [[ ! "$_arg" =~ ^account[s]?${global_arg_delim_2} ]] \ && [[ ! "$_arg" =~ ^year[s]?${global_arg_delim_2} ]] \ && lib_utils::die_usage "$_usage" done # # Parse arguments before testing # # Parse key for value for _arg in "$@"; do local _key="${_arg%${global_arg_delim_2}*}" local _len="$((${#_key} + 1))" if [[ "$_key" =~ ^type[s]?$ ]]; then local _arg_type="${_arg:${_len}}" [ -z "$_arg_type" ] && lib_utils::die_usage "$_usage" fi if [[ "$_key" =~ ^account[s]?$ ]]; then local _arg_account="${_arg:${_len}}" [ -z "$_arg_account" ] && lib_utils::die_usage "$_usage" fi if [[ "$_key" =~ ^year[s]?$ ]]; then local _arg_year="${_arg:${_len}}" [ -z "$_arg_year" ] && lib_utils::die_usage "$_usage" fi done # # Test for valid ordering/functionality of argument values # # Arg: account if [ ! -z "$_arg_account" ]; then if [ -z "$_arg_type" ]; then lib_utils::die_usage "$_usage" fi fi # Arg: year if [ ! -z "$_arg_year" ]; then if [[ -z "$_arg_type" ]]; then lib_utils::die_usage "$_usage" fi fi # # Test argument values, set globals # IFS="$global_arg_delim_3" # Arg: type if [ ! -z "$_arg_type" ]; then read -ra _read <<<"$_arg_type" for _type in "${_read[@]}"; do if [[ ! "$_type" =~ ^fetch$|^iadd$|^manual$|^meta$|^preprocess$|^rules$|^shell$ ]]; then lib_utils::die_usage "$_usage" fi if [[ ! -z "$_arg_account" ]]; then if [[ ! "$_type" =~ ^preprocess$|^rules$ ]]; then lib_utils::die_usage "$_usage" fi fi if [[ -z "$_arg_account" ]]; then if [[ "$_type" =~ ^preprocess$|^rules$ ]]; then lib_utils::die_usage "$_usage" fi fi done declare -gr global_arg_type=("${_read[@]}") fi # Arg: account if [ ! -z "$_arg_account" ]; then read -ra _read <<<"$_arg_account" declare -gr global_arg_account=("${_read[@]}") fi # Arg: year # TODO: implement range if [ ! -z "$_arg_year" ]; then # TODO: 20th century support if [[ ! "$_arg_year" =~ ^20[0-9][0-9]$ || ! ${global_arg_type[*]} =~ ^manual$ ]]; then lib_utils::die_usage "$_usage" fi declare -gr global_arg_year="$_arg_year" else global_arg_year="$(date +%Y)" # Set default current declare -gr global_arg_year fi } function lib_edit::__edit() { [ -z "$EDITOR" ] && lib_utils::die_fatal [ -z "$global_child_profile" ] && lib_utils::die_fatal [ -z "$global_child_profile_flow" ] && lib_utils::die_fatal [ -z "$global_conf_fetch" ] && lib_utils::die_fatal [ -z "$global_conf_meta" ] && lib_utils::die_fatal [ -z "$global_conf_shell" ] && lib_utils::die_fatal for _type in "${global_arg_type[@]}"; do case "$_type" in fetch) $EDITOR "$global_conf_fetch" ;; iadd | manual) local _path="${global_child_profile_flow}/import/${global_child_profile}/_manual_/${global_arg_year}" [ ! -d "$_path" ] && mkdir -p "$_path" _path+="/post-import.journal" [ ! -f "$_path" ] && touch "$_path" # TODO: upstream request to provide comment(N) entries [[ "$_type" == "iadd" ]] && /usr/bin/hledger-iadd -f "$_path" # TODO: option for editor type [[ "$_type" == "manual" ]] && $EDITOR "$_path" ;; meta) # NOTE: # - Expects comments to begin with format: //! # - If saved to original, opening with `--skip` will clobber the # original file's comments. local -r _skip="$(grep -E "^//!" $global_conf_meta | wc -l)" visidata --quitguard --motd-url file:///dev/null --filetype csv --skip "$_skip" "$global_conf_meta" # TODO: HACK: visidata saves w/ DOS-style carriage... # ...but there seems to be no option out of this. sed -i 's:\r::g' "$global_conf_meta" ;; preprocess | rules) # Run all paths through one editor instance local _paths=() # Prepare account/account-shared.* or account/subaccount/account-subaccount.* for _account in "${global_arg_account[@]}"; do local _acct="${_account%${global_arg_delim_1}*}" # Attempting to parse out an empty arg will result in dup account as sub [[ "$_account" =~ ${global_arg_delim_1} ]] \ && local _sub="${_account#*${global_arg_delim_1}}" local _path="${global_child_profile_flow}/import/${global_child_profile}/" [ ! -z "$_sub" ] && _path+="${_acct}/${_sub}/" case "$_type" in preprocess) [ -z "$_sub" ] \ && local _file="${_acct}-shared.bash" \ || local _file="$_type" ;; rules) [ -z "$_sub" ] \ && local _file="${_acct}-shared.${_type}" \ || local _file="${_acct}-${_sub}.${_type}" ;; esac _path+="$_file" if [ ! -f "$_path" ]; then lib_utils::die_fatal "File not found: $_path" else _paths+=("$_path") fi done # Execute $EDITOR "${_paths[@]}" ;; shell) $EDITOR "$global_conf_shell" ;; esac done } # vim: sw=2 sts=2 si ai et