# HG changeset patch
# User Raphaël Gomès <rgomes@octobus.net>
# Date 1678880075 -3600
#      Wed Mar 15 12:34:35 2023 +0100
# Node ID 8e4c05833113d9f6fe4ec45d976f8edfc54a827c
# Parent  07297ef82aa5f7752e3955c3ee9c1c8f71508a2d
bin: rewrite `bin-env-util` with `click`

This is much simpler and more powerful than rolling it by hand.

diff --git a/python-libs/poulpe/bin/bin-env-util b/python-libs/poulpe/bin/bin-env-util
--- a/python-libs/poulpe/bin/bin-env-util
+++ b/python-libs/poulpe/bin/bin-env-util
@@ -1,10 +1,9 @@
 #!/usr/bin/env python3
-#
-# Small tool to manipulate bin-envs
-import argparse
 import os
+import sys
 import subprocess
-import sys
+
+import click
 
 import poulpe
 
@@ -18,7 +17,7 @@
 err = poulpe.err
 
 
-def create_empty(env_path, method="manual"):
+def _create_empty(env_path, method="manual"):
     """create a new environ (in a non-ready state)"""
     os.makedirs(env_path, exist_ok=True)
     base_data = {
@@ -32,28 +31,67 @@
     file_path = poulpe.bin_env_file(env_path)
     if os.path.exists(file_path):
         err('This is already a bin-env, aborting')
-        return ERR_CODE_EXISTS
+        sys.exit(ERR_CODE_EXISTS)
     poulpe.write_data(file_path, base_data)
-    return 0
+
+
+def _mark_ready(path):
+    shell_path = os.path.join(path, poulpe.SHELL_FILE)
+    if not os.access(shell_path, os.X_OK):
+        err(f'cannot find an executable file at: "{shell_path}"')
+        sys.exit(ERR_CODE_NO_BIN_ENV_SHELL)
+
+    path = poulpe.bin_env_file(path)
+    data = poulpe.get_data(path)
+    if data is None:
+        err(f'missing file: "{path}"')
+        sys.exit(ERR_CODE_NO_BIN_ENV_DESC)
+    data['ready'] = True
+    poulpe.write_data(path, data)
+
+
+@click.group(no_args_is_help=True)
+def bin_env_util():
+    """Tool to manipulate binary environments"""
 
 
-def setup_one(env_path, script):
+@bin_env_util.command(no_args_is_help=True)
+@click.argument("path")
+def create_empty(path):
+    """Create a new empty bin-env at a given path
+
+    \b
+    ARGS:
+        <PATH> is the path of the environment to create
+    """
+    _create_empty(path)
+
+
+@bin_env_util.command(no_args_is_help=True)
+@click.argument("path")
+@click.argument("script")
+def setup_one(path, script):
+    """Create a new bin-env with a setup script.
+
+    \b
+    ARGS:
+        <PATH> is the path of the environment to create
+        <SCRIPT> is the path to the script to setup the environment
+    """
     if not os.access(script, os.X_OK):
         err(f'no executable setup script at: "{script}"')
-        return ERR_CODE_NO_SETUP_SCRIPT
-    ret = create_empty(env_path, method="script")
-    if ret != 0:
-        return ret
+        sys.exit(ERR_CODE_NO_SETUP_SCRIPT)
+    _create_empty(path, method="script")
     script = os.path.abspath(script)
     try:
-        subprocess.check_call(script, cwd=env_path)
+        subprocess.check_call(script, cwd=path)
     except subprocess.CalledProcessError as exc:
         err(f'script returned with status {exc.returncode}: {script}')
-        return ERR_CODE_SETUP_SCRIPT_FAILED
+        sys.exit(ERR_CODE_SETUP_SCRIPT_FAILED)
 
-    env_file = poulpe.bin_env_file(env_path)
+    env_file = poulpe.bin_env_file(path)
     data = poulpe.get_data(env_file)
-    vars_file = os.path.join(env_path, "POULPE-VARS")
+    vars_file = os.path.join(path, "POULPE-VARS")
     if os.path.exists(vars_file):
         with open(vars_file) as f:
             for line in f:
@@ -63,174 +101,99 @@
                 k = f'bin-env-vars.{k}'
                 poulpe.set_one_value(data, k, v)
     poulpe.write_data(env_file, data)
-    return mark_ready(env_path)
+    _mark_ready(path)
 
 
-def mark_ready(env_path):
-    shell_path = os.path.join(env_path, poulpe.SHELL_FILE)
-    if not os.access(shell_path, os.X_OK):
-        err(f'cannot find an executable file at: "{shell_path}"')
-        return ERR_CODE_NO_BIN_ENV_SHELL
+@bin_env_util.command(no_args_is_help=True)
+@click.argument("path")
+def mark_ready(path):
+    """Mark a bin-env as ready.
 
-    path = poulpe.bin_env_file(env_path)
-    data = poulpe.get_data(path)
-    if data is None:
-        err(f'missing file: "{path}"')
-        return ERR_CODE_NO_BIN_ENV_DESC
-    data['ready'] = True
-    poulpe.write_data(path, data)
-    return 0
+    \b
+    ARGS:
+        <PATH> is the path of the environment to mark as ready
+    """
+    _mark_ready(path)
 
 
-def show(env_path):
-    path = poulpe.bin_env_file(env_path)
+@bin_env_util.command(no_args_is_help=True)
+@click.argument("path")
+def show(path):
+    """Show all data we have about this environment.
+
+    \b
+    ARGS:
+        <PATH> is the path of the environment to show
+    """
+    path = poulpe.bin_env_file(path)
     data = poulpe.get_data(path)
     if data is None:
         err(f'missing file: "{path}"')
-        return ERR_CODE_NO_BIN_ENV_DESC
+        sys.exit(ERR_CODE_NO_BIN_ENV_DESC)
     poulpe.show(data)
-    return 0
+
+
+@bin_env_util.command("get", no_args_is_help=True)
+@click.argument("path")
+@click.argument("key")
+def get_value(path, key):
+    """Show a specific bin-env variable.
+
+    \b
+    ARGS:
+        <PATH> is the path of the environment
+        <KEY> is the path to the variable
+    """
+    f = poulpe.bin_env_file(path)
+    data = poulpe.get_data(f)
+    if data is None:
+        err(f'missing file: "{f}"')
+        sys.exit(ERR_CODE_NO_BIN_ENV_DESC)
+    k = f'bin-env-vars.{key}'
+    value = poulpe.get_one_value(data, k)
+    if value is None:
+        sys.exit(ERR_CODE_NO_KEY)
+    print(value)
 
 
-def _parsers():
-    top_parser = argparse.ArgumentParser(prog='poulpe-bin-env-util')
-    subparsers = top_parser.add_subparsers(
-        help='available sub-command', dest='command', required=True
-    )
-
-    # XXX having PATH everywhere is not great
-
-    ### bin-env-util create-empty PATH
-    cmd_parser = subparsers.add_parser(
-        'create-empty', help='create a new empty bin-env'
-    )
-    cmd_parser.add_argument(
-        'PATH',
-        help="the path to the (future) environment",
-    )
-
-    ### bin-env-util setup-one-sh PATH SCRIPT
-    cmd_parser = subparsers.add_parser(
-        'setup-one', help='create a new bin-env with a setup script'
-    )
-    cmd_parser.add_argument(
-        'PATH',
-        help="the path of the environment",
-    )
-    cmd_parser.add_argument(
-        'SCRIPT',
-        help="the path to the script to setup the env",
-    )
-
-    ### bin-env-util mark-ready PATH
-    cmd_parser = subparsers.add_parser(
-        'mark-ready', help='mark a bin-env as ready'
-    )
-    cmd_parser.add_argument(
-        'PATH',
-        help="the path to the environment",
-    )
+@bin_env_util.command("set", no_args_is_help=True)
+@click.argument("path")
+@click.argument("key")
+@click.argument("value")
+def set_value(path, key, value):
+    """Set the value of a specific bin-env variable.
 
-    ### bin-env-util show PATH
-    cmd_parser = subparsers.add_parser(
-        'show', help='Show all data we have about this environment'
-    )
-    cmd_parser.add_argument(
-        'PATH',
-        help="the path to the environment",
-    )
-
-    ### bin-env-util get PATH KEY
-    cmd_parser = subparsers.add_parser(
-        'get', help='Show a specific bin-env variable'
-    )
-    cmd_parser.add_argument(
-        'PATH',
-        help="the path to the environment",
-    )
-    cmd_parser.add_argument(
-        'KEY',
-        help="the path to the variable",
-    )
-
-    ### bin-env-util set PATH KEY VALUE
-    cmd_parser = subparsers.add_parser(
-        'set', help='Set the value of a specific bin-env variable'
-    )
-    cmd_parser.add_argument(
-        'PATH',
-        help="the path to the environment",
-    )
-    cmd_parser.add_argument(
-        'KEY',
-        help="the path to the variable",
-    )
-    cmd_parser.add_argument(
-        'VALUE',
-        help="the value to set",
-    )
-
-    ### bin-env-util del PATH KEY
-    cmd_parser = subparsers.add_parser(
-        'del', help='Delete a specific bin-env variable'
-    )
-    cmd_parser.add_argument(
-        'PATH',
-        help="the path to the environment",
-    )
-    cmd_parser.add_argument(
-        'KEY',
-        help="the path to the variable",
-    )
-
-    return top_parser
+    \b
+    ARGS:
+        <PATH> is the path of the environment
+        <KEY> is the path to the variable
+        <VALUE> is the value to be set
+    """
+    f = poulpe.bin_env_file(path)
+    k = f'bin-env-vars.{key}'
+    data = poulpe.get_data(f)
+    if data is None:
+        err(f'creating new file: "{f}"')
+        data = {}
+    poulpe.set_one_value(data, k, value)
+    poulpe.write_data(f, data)
 
 
-def main(args):
-    parser = _parsers()
-    param = parser.parse_args(args)
-    if param.command == 'create-empty':
-        ret = create_empty(param.PATH)
-    elif param.command == 'setup-one':
-        ret = setup_one(param.PATH, param.SCRIPT)
-    elif param.command == 'mark-ready':
-        ret = mark_ready(param.PATH)
-    elif param.command == 'show':
-        ret = show(param.PATH)
-    elif param.command == 'get':
-        f = poulpe.bin_env_file(param.PATH)
-        data = poulpe.get_data(f)
-        if data is None:
-            err(f'missing file: "{f}"')
-            ret = ERR_CODE_NO_BIN_ENV_DESC
-        else:
-            k = f'bin-env-vars.{param.KEY}'
-            value = poulpe.get_one_value(data, k)
-            if value is None:
-                ret = ERR_CODE_NO_KEY
-            else:
-                print(value)
-                ret = 0
-    elif param.command == 'set':
-        f = poulpe.bin_env_file(param.PATH)
-        k = f'bin-env-vars.{param.KEY}'
-        data = poulpe.get_data(f)
-        if data is None:
-            err(f'creating new file: "{f}"')
-            data = {}
-        poulpe.set_one_value(data, k, param.VALUE)
-        poulpe.write_data(f, data)
-        return 0
-    elif param.command == 'del':
-        f = poulpe.bin_env_file(param.PATH)
-        k = f'bin-env-vars.{param.KEY}'
-        ret = poulpe.del_value(f, k)
-    else:
-        assert False
-    return ret
+@bin_env_util.command("del", no_args_is_help=True)
+@click.argument("path")
+@click.argument("key")
+def del_value(path, key):
+    """Delete a specific bin-env variable.
+
+    \b
+    ARGS:
+        <PATH> is the path of the environment
+        <KEY> is the path to the variable to delete
+    """
+    f = poulpe.bin_env_file(path)
+    k = f'bin-env-vars.{key}'
+    poulpe.del_value(f, k)
 
 
 if __name__ == "__main__":
-    ret = main(sys.argv[1:])
-    assert ret is not None
-    sys.exit(ret)
+    bin_env_util()
diff --git a/tests/test-bin-env-util.t b/tests/test-bin-env-util.t
--- a/tests/test-bin-env-util.t
+++ b/tests/test-bin-env-util.t
@@ -4,23 +4,21 @@
 Check basic invocation
 
   $ poulpe bin-env-util --help
-  usage: poulpe-bin-env-util [-h]
-                             {create-empty,setup-one,mark-ready,show,get,set,del}
-                             ...
+  Usage: bin-env-util [OPTIONS] COMMAND [ARGS]...
+  
+    Tool to manipulate binary environments
+  
+  Options:
+    --help  Show this message and exit.
   
-  positional arguments:
-    {create-empty,setup-one,mark-ready,show,get,set,del}
-                          available sub-command
-      create-empty        create a new empty bin-env
-      setup-one           create a new bin-env with a setup script
-      mark-ready          mark a bin-env as ready
-      show                Show all data we have about this environment
-      get                 Show a specific bin-env variable
-      set                 Set the value of a specific bin-env variable
-      del                 Delete a specific bin-env variable
-  
-  option*s: (glob)
-    -h, --help            show this help message and exit
+  Commands:
+    create-empty  Create a new empty bin-env at a given path
+    del           Delete a specific bin-env variable.
+    get           Show a specific bin-env variable.
+    mark-ready    Mark a bin-env as ready.
+    set           Set the value of a specific bin-env variable.
+    setup-one     Create a new bin-env with a setup script.
+    show          Show all data we have about this environment.
 
 
 Check repository creation