/home/alex/dev/lib/nf.sh (1)

From RaySoft
# ------------------------------------------------------------------------------
# nf.sh
# =====
#
# Project   Library
# Scope     macOS
# Copyright (C) 2024 by RaySoft, Zurich, Switzerland
# License   GNU General Public License (GPL) 2.0
#           https://www.gnu.org/licenses/gpl2.txt
#
# ------------------------------------------------------------------------------

# Exits if the library is run as a script and not sourced as a library
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
  echo "Don't run this script! Just source it."
  exit 1
fi

# Returns if the library is already loaded
if declare -F 'nf' >'/dev/null' 2>&1; then
  return 0
fi

# ------------------------------------------------------------------------------

# The prompt printed before the command line is echoed when the -x option is set.
PS4='\n$(nf::runtime_position 0):  '

# ------------------------------------------------------------------------------

nf() {
  # Shows all function of this library.
  #
  # Arguments:
  #   none
  #
  # Returns:
  #   0:  Success
  #   >0: Error

  nf::print_heading '-' 'ba' "My 'Notification' Library"

  # Prints all functions starting with the prefix 'xx::' but ignore private
  # functions with the prefix 'xx::_'
  compgen -c "${FUNCNAME[0]}::" | grep -v "${FUNCNAME[0]}::_"

  echo

  return 0
}

# ------------------------------------------------------------------------------

nf::carp() {
  # Shows the exact position of an error, prints the error message and
  # optionally exits the script.
  # The name of this function was derived from the Perl module "Carp".
  #
  # Arguments:
  #   $1: Parameter '-e' with error number (optional)
  #   $@: Error message
  #
  # Returns:
  #   0:  Success
  #   >0: Error
  #
  # Example:
  #   nf::carp -e2 'Error running AppleScript!'

  # Print the runtime position of the script
  if ! nf::runtime_position 1; then
    return 1
  fi

  # If the first argument starts with '-e' followed by a number
  if [[ "$1" =~ ^-e([0-9]*)$ ]]; then
    # Prints the error message
    echo ": ${*:2}"

    # Exits the script
    exit "${BASH_REMATCH[1]:-1}"
  else
    # Prints the error message
    echo ": $*"
  fi

  return 0
} 1>&2

# ------------------------------------------------------------------------------

nf::notify_macos() {
  # Sends a notification to the macOS notification center.
  #
  # Arguments:
  #   $1: Title of the message
  #   $2: Sub title of the message (optional)
  #   $@: Message (optional)
  #
  # Returns:
  #   0:  Success
  #   >0: Error
  #
  # Example:
  #   nf::notify_macos 'Backup' 'Backup ended successfully!' "$(date '+%F %H:%M')"

  # Returns if the function was called incorrectly
  if [[ "$#" -eq 0 ]]; then
    nf::carp 'Error calling function!'
    return 1
  fi

  # Returns if a needed binary is not available
  for bin in 'osascript'; do
    if ! type -P "${bin}" >'/dev/null' 2>&1; then
      nf::carp "Error finding binary: ${bin}!"
      return 1
    fi
  done

  # Defines a local variable to put together the AppleScript
  local script="display notification"

  # Adds the message if it is available
  if [[ "$#" -gt 2 ]]; then
    script+=" \"${*:3}\""
  fi

  # Adds the title
  script+=" with title \"$1\""

  # Adds the subtitle if it is available
  if [[ "$#" -gt 1 ]]; then
    script+=" subtitle \"$2\""
  fi

  # Runs the AppleScript
  if ! osascript -e "${script}" >'/dev/null' 2>&1; then
    nf::carp 'Error running AppleScript!'
    return 1
  fi

  return 0
}

# ------------------------------------------------------------------------------

nf::print_heading() {
  # Prints a heading.
  #
  # Arguments:
  #   $1: Type of underline ('-', '=' or '+')
  #   $2: New line before (b) or after (a) the heading. '-' for no new lines.
  #   $@: Message
  #
  # Returns:
  #   0:  Success
  #   >0: Error
  #
  # Example:
  #   nf::print_heading '=' 'ba' 'Heading'

  # Returns if the function was called incorrectly
  if [[ "$#" -lt 3 || ! "$1" =~ ^[-=+]$ || ! "$2" =~ ^(-|[ab]{1,2})$ ]]; then
    nf::carp 'Error calling function!'
    return 1
  fi

  # Prints a newline if the second argument ($2) contains a 'b'
  [[ "$2" =~ b ]] && echo

  # Prints the heading message and underlines it with character provides in the
  # first argument ($1)
  if ! sed "p; s/./$1/g" <<<"${*:3}" 2>'/dev/null'; then
    nf::carp 'Error printing heading!'
    return 1
  fi

  # Prints a newline if the second argument ($2) contains a 'a'
  [[ "$2" =~ a ]] && echo

  return 0
}

# ------------------------------------------------------------------------------

nf::runtime_position() {
  # Print the runtime position of a script.
  #
  # Arguments:
  #   $1: Number of elements to be ignored at the end of the 'FUNCNAME' array
  #
  # Returns:
  #   String

  # Returns if the function was called incorrectly
  if [[ "$#" -ne 1 || ! "$1" =~ ^[0-9]+$ ]]; then
    echo 'Error calling function!'
    return 1
  fi

  # Defines local variables
  local i=0

  # For all elements in the array 'FUNCNAME' in reverse order
  for ((i = "${#FUNCNAME[@]}" - 1; i > "$1"; i--)); do
    # If the function name is 'main' or 'source'
    if [[ "${FUNCNAME[$i]}" =~ ^main|source$ ]]; then
      # Prints the script name
      echo -n "${BASH_SOURCE[$i]##*/}"
    else
      # Prints the function name
      echo -n "${FUNCNAME[$i]}"
    fi

    # Prints the corresponding line number
    echo -n "[${BASH_LINENO[$((i - 1))]}]"

    # Prints a separator if it is not the last iteration
    [[ "$i" -gt $(( "$1" + 1 )) ]] && echo -n '>'
  done

  return 0
}

# ------------------------------------------------------------------------------

nf::tail() {
  # Opens a new iTerm window and tails a file in it.
  #
  # Arguments:
  #   $1: File
  #
  # Returns:
  #   0:  Success
  #   >0: Error
  #
  # Example:
  #   nf::tail '/var/log/system.log'

  # Returns if the function was called incorrectly
  if [[ "$#" -ne 1 ]]; then
    nf::carp 'Error calling function!'
    return 1
  fi

  local bin=''

  # Returns if a needed binary is not available
  for bin in 'osascript'; do
    if ! type -P "${bin}" >'/dev/null' 2>&1; then
      nf::carp "Error finding binary: ${bin}!"
      return 1
    fi
  done

  # Returns if the file is not available
  if [[ ! -f "$1" ]]; then
    nf::carp "Error finding file: $1!"
    return 1
  fi

  # Runs the AppleScript to open a new iTerm windows and executes the 'tail'
  # command in it
  if ! osascript <<-EOS >'/dev/null' 2>&1; then
		tell application "iTerm"
		  set newWindow to (create window with default profile)
		  tell newWindow
		    tell current session of newWindow to write text "tail -f -n '+1' '$1'"
		  end tell
		end tell
	EOS
    nf::carp 'Error running AppleScript!'
    return 1
  fi

  return 0
}

# ------------------------------------------------------------------------------

return 0

Usage

# Path to the global libraries
XX_DE_GLOBAL_LIB="${HOME}/dev/lib"

# Global libraries to be loaded
XX_GLOBAL_LIBS=('nf')
# Test if library path is available
if [[ ! -d "${XX_DE_GLOBAL_LIB}" ]]; then
  echo "Error finding directory: ${XX_DE_GLOBAL_LIB}!"
  exit 1
fi

# Load libraries
for lib in "${XX_GLOBAL_LIBS[@]}"; do
  lib="${XX_DE_GLOBAL_LIB}/${lib}.sh"

  if [[ ! -f "${lib}" ]]; then
    echo "Error finding library: ${lib}!"
    exit 1
  fi

  if ! source "${lib}"; then
    exit 1
  fi
done