#!/bin/bash
# dev/gen/bash - MCSH bash script source generation

set -e

######
# Library Configuration

dev_gen_bash_config_init() {
	# $gen_bash_namespace - If set, specifies the namespace to use
	# when generating library script functions.  If not set, the
	# basename of the generated library will be used automatically.
	lib_setting_vars --null gen_bash_namespace

	# $gen_bash_here_pipe[] - Specifies the pipeline of commands
	# that should be used to encode/decode the content in a here
	# document function.  The commands should be specified in the
	# order required for encoding.
	#
	# For decoding, the command pipeline is built in reverse order,
	# and each command gets passed one additional option: ``-d``.
	lib_setting_arrays gen_bash_here_pipe
	[ ${#gen_bash_here_pipe[*]} -gt 0 ] \
		|| gen_bash_here_pipe=( 'gzip -c' 'base64' )
}


######
# MCSH Bash Script CLI

# gen_bash_dispatch() - Dispatches a Bash script generation command ($@)
gen_bash_dispatch() { lib_cmd_dispatch gen_bash "$@"; }

# gen_bash_usage() - Prints usage for Bash script generation commands
gen_bash_usage() {
	cat <<USAGE
<cmd> ...

Source Generation Commands:
	tool <name> <desc> [<lib>+]	Generates a new tool
	lib <name> <desc> [<lib>+]	Generates a new library
	license <blurb> <wrnty> <lic>	Generates the 'license' library

Low-Level Source Generation Commands:
	header <name> <desc>		Prints standard library header code
	func <name>			Generates a function
	here doc <tag>			Generates here document
	here func <tag>			Generates here document function
USAGE
}

# gen_bash_source() - Generates Bash source file(s) for the given kind ($1),
# name ($2), description ($3), and (optional) dependencies ($@).
gen_bash_source() {
	min_args 2 "$@"
	local kind=$1
	local name=$2
	local desc=$3

	local gen_edit=true
	local gen_error_fail=true
	local conf dir
	case "$kind" in
	(tool)
		gen_bash_source "conf" "$name.conf" "$desc configuration"
		conf="conf/$name.conf.in"
		dir=src
		;;
	(conf)
		gen_edit=false
		gen_error_fail=false
		dir=conf
		;;
	(library)
		dir=libs
		;;
	esac

	local file="$dir/$name.in"
	[ ! -f "$file" ] || error "$file: exists"

	gen_bash_source_file "$@" >"$file"

	local -a files=( "$file" )
	[ -z "$conf" ] || files+=( "$conf" )
	! $gen_edit || run_editor "${files[@]}"

	if [ ! -s "$file" ]; then
		warn "$file: $kind empty... aborting"
		run rm -f "${files[@]}"
	fi
}

#; internal helper
gen_bash_source_file() {
	min_args 3 "$@"
	local kind=$1
	local name=$2
	local desc=$3
	shift 3

	gen_bash_header "$name" "$desc"

	if [ "$kind" = tool ]; then
		echo
		echo "source '__""runtime""__'"
	fi

	if [ "$*" ]; then
		echo
		for_each gen_bash_lib_load "$@"
	fi

	gen_bash_source_$kind "$name" "$desc"
}


######
# MCSH Functions

# gen_bash_lib_load() - Generates a command to load a named library script ($1)
gen_bash_lib_load() { echo "lib_load '$1'"; }


######
# MCSH Tool Scripts

# gen_bash_tool() - Generates template source code for a new tool ($1)
# given its description ($2) and (optional) dependencies ($@).
gen_bash_tool() { gen_bash_source tool "$@"; }

# gen_bash_source_tool() - Generates the body of a new tool script ($1).
gen_bash_source_tool() {
	local path=$1
	local desc=$2
	local name=${path##*/}
	local sym=$(lib_symbol "$name")

#---
	cat <<TOOL


######
# Native Dependencies

${sym}_client_packages=( )
${sym}_server_packages=( )


######
# Script Initialization

# ${sym}_init() - Performs script-specific initialization.
${sym}_init() {
	: debug "${sym}_init called"
}


######
# Script Configuration

# ${sym}_config_init() - Defines, documents, and initializes script settings.
${sym}_config_init() {
	# ${sym}_var - Documents this setting variable
	lib_setting_vars ${sym}_var
}

# ${sym}_config_check() - Initializes remaining script settings and
# validates their values.
${sym}_config_check() {
	${sym}_var=\${${sym}_var:-true}
}


######
# CLI

# ${sym}_func() - Does not do what this documentation says it does
${sym}_func() {
	warn "$name has not been implemented"  # if true, what is this?!
}


######
# Check

# ${sym}_check() - Performs a self-check of all script functionality
${sym}_check() {
	${sym}_func
}


######
# Main

# ${sym}_desc() - Prints a short description of the $name script
${sym}_desc() { echo "$desc"; }

# ${sym}_usage() - Prints the top-level command usage for the $name script
${sym}_usage() {
	cat <<USAGE
...
$sym Commands:
	func				Tests the $name tool
USAGE
}

# ${sym}_help() - Prints the top-level command help for this script
${sym}_help() {
	cat <<HELP
$name is a $desc tool.  It is part of the $package_name package.
HELP
}

app_run "\$@"
TOOL
#+++
}


######
# MCSH Tool Configuration Scripts

# gen_bash_source_conf() - Generates a tool configuration file.
gen_bash_source_conf() {
	:
}

######
# MCSH Library Scripts

# gen_bash_lib() - Generates template source code for a new library ($1)
# given its description ($2) and (optional) dependencies ($@).
gen_bash_lib() { gen_bash_source library "$@"; }

# gen_bash_source_library() - Generates the body of a new library script ($1)
# with the given description ($2).
gen_bash_source_library() {
	local name=$1
	local desc=$2

	local sym=$(lib_symbol "$name")
	local ns=${gen_bash_namespace:-$(lib_symbol "${name##*/}")}

#---
	cat <<LIB


######
# Native Dependencies

${sym}_client_packages=( )
${sym}_server_packages=( )


######
# Library Initialization

# ${sym}_lib_init() - Performs global library initialization.
${sym}_lib_init() {
	: debug "${sym}_lib_init called"
}


######
# Library Configuration

# ${sym}_config_init() - Defines, documents, and initializes library settings.
${sym}_config_init() {
	: debug "${sym}_config_init called"
}

# ${sym}_config_check() - Initializes remaining library settings and
# validates their values.
${sym}_config_check() {
	: debug "${sym}_config_check called"
}


######
# $name CLI

# ${ns}_dispatch() - Initializes remaining library settings and
${ns}_dispatch() { lib_cmd_dispatch ${ns} "\$@"; }

# ${ns}_usage() - Prints the top-level command usage for the $name script
${ns}_usage() {
	cat <<USAGE
...
Commands:
	func				Tests $ns command dispatch
USAGE
}


######
# Public Interface

# ${ns}_func() - Does not do what this documentation says it does
${ns}_func() {
	warn "$name library has not been implemented"  # if true, what is this?!
}
LIB
#+++
}


######
# License Library

# gen_bash_license() - Generates the license library from the named files.
# $1 - Name of the package (e.g. $(PKG))
# $2 - Name of file containing license blurb (e.g. LICENSE)
# $3 - Name of file containing warranty information (e.g. WARRANTY)
# $4 - Name of file containing full license (e.g. COPYING)
gen_bash_license() {
	has_args 4 "$@"
	gen_bash_header "license-parts" "$1 licence details"
	gen_bash_license_func blurb "summary" <"$2"
	gen_bash_license_func warranty "warrany information "<"$3"
	gen_bash_license_func full <"$4"
}

# gen_bash_license_func() - Generates a single here document function
# using the given function ($1) with the given description ($2).
# $1 - Name of generated license function
# $2 - Description of license function (optional)
gen_bash_license_func() {
	min_max_args 1 2 "$@"

	echo

	local func="license_${1}_print"
	gen_bash_here_func "$func" "Prints the package license${2:+ }$2."
}


######
# Source File Headers

# gen_bash_header() - Generates a header for a Bash script.
gen_bash_header() {
	has_args 2 "$@"
	local name=$1
	local desc=$2

#---
	cat <<EOF
#!/bin/bash
# $name - $desc
EOF
#+++
	$gen_error_fail || return 0
	echo
	echo "set -e"
}


######
# Here Document Functions

# gen_bash_here_func() - Generates a function ($1) containing a single
# here document ($2).
#
# By default, the here document content gets compressed and encoded to
# reduce the space of the final distributable.  This can be disabled
# by adding the following line in your configuration:
#
#   ``gen_bash_here_pipe=( cat )``
#
# To extract the original content, the generated function will pipe the
# encoded document through the same decoding and decompression
# programs when called.  Consequently, those native dependencies must be
# installed on the system.
#
# $1 - Name of function.
# $2 - Function documentation string.
# $3 - Name of file (optional); otherwise, document will be read from `stdin`.
gen_bash_here_func() {
	min_max_args 2 3 "$@"
	local name=$1
	local docs=$2
	local file=${3:--}

	local -a decode_pipe=( $(gen_bash_here_decode_pipe) )

	local -u tag="${name}_data"
	cat "$file" \
		| eval "$(gen_bash_here_encode_pipe)" \
		| gen_bash_here_doc "$tag" - ${decode_pipe[*]} \
		| gen_bash_func "$name" "$docs"
}

# gen_bash_here_encode_pipe() - Prints the command pipeline for encoding
# the content of a here document.  See ``$gen_bash_here_pipe[]``.
gen_bash_here_encode_pipe() {
	gen_bash_here_pipe "${gen_bash_here_pipe[@]}"
}

# gen_bash_here_decode_pipe() - Prints the command pipeline for decoding an
# encoded here document.  See ``$gen_bash_here_pipe[]``.
gen_bash_here_decode_pipe() {
	local -a cmds=( "${gen_bash_here_pipe[@]}" )
	list_reverse cmds
	list_map gen_bash_here_decode_cmd cmds
	echo '|'
	gen_bash_here_pipe "${cmds[@]}"
}

# gen_bash_here_decode_cmd() - Appends decode option to each pipeline command.
gen_bash_here_decode_cmd() { echo "$* -d"; }

# gen_bash_here_pipe() - Prints a command pipline, constructed from
# the given commands ($@), that can be ``eval``-uated by the shell.
gen_bash_here_pipe() {
	local i result=""
	for i in "$@"; do
		result="$result${result:+ | }$i"
	done
	echo "$result"
}


######
# Functions

# gen_bash_func() - Generates a function ($1) from the given file ($2).
#
# $1 - Name of function.
# $2 - Function documentation.  If `null`, no documentation will be
# generated for the new function.
# $3 - Name of file containing the function body (optional).  If not
# given, the function body will be read from `stdin`.
gen_bash_func() {
	min_max_args 2 3 "$@"
	local name=$1
	local docs=$2
	local file=${3:--}
	[ -z "$docs" ] || echo "# $name() - $docs"
	echo "$name() {"
	cat "$file"
	echo "}"
}


######
# Here Documents

# gen_bash_here_doc() - Generates a here document with the given tag
# ($1) from the given file ($2).
# $1 - Tag to use as delimiter.
# $2 - Name of file containing here document content.  Use ``-`` to use `stdin`.
# $@ - Command pipeline to use for decoding content.
gen_bash_here_doc() {
	min_args 2 "$@"
	local tag=$1
	local file=$2
	shift 2
	echo "cat <<$tag" "$@"
	cat "$file"
	echo "$tag"
}

View the Developer Guide Index

View the Reference Manual Index


Generated on Tue Jul 4 17:00:32 PDT 2017 by mcsh d14 v0.21.0.