""" System utility functions """

import os

__author__ = "Databio Lab"
__email__ = "nathan@code.databio.org"

__all__ = ["is_command_callable", "is_writable"]


def is_command_callable(cmd):
    """
    Check if command can be called.

    :param str cmd: actual command to check for callability
    :return bool: whether given command's call succeeded
    :raise TypeError: if the alleged command isn't a string
    :raise ValueError: if the alleged command is empty
    """
    if not isinstance(cmd, str):
        raise TypeError("Alleged command isn't a string: {} ({})")
    if not cmd:
        raise ValueError("Empty command to check for callability")
    if os.path.isdir(cmd) or (os.path.isfile(cmd) and not os.access(cmd, os.X_OK)):
        return False
    # Use `command` to see if command is callable, and rule on exit code.
    check = "command -v {0} >/dev/null 2>&1 || {{ exit 1; }}".format(cmd)
    return not bool(os.system(check))


def is_writable(folder, check_exist=False, create=False):
    """
    Make sure a folder is writable.

    Given a folder, check that it exists and is writable. Errors if requested on
    a non-existent folder. Otherwise, make sure the first existing parent folder
    is writable such that this folder could be created.

    :param str folder: Folder to check for writeability.
    :param bool check_exist: Throw an error if it doesn't exist?
    :param bool create: Create the folder if it doesn't exist?
    """
    folder = folder or "."

    if os.path.exists(folder):
        return os.access(folder, os.W_OK) and os.access(folder, os.X_OK)
    elif create:
        os.mkdir(folder)
    elif check_exist:
        raise OSError("Folder not found: {}".format(folder))
    else:
        # The folder didn't exist. Recurse up the folder hierarchy to make sure
        # all paths are writable
        return is_writable(os.path.dirname(folder), check_exist)
