aboutsummaryrefslogtreecommitdiff
path: root/prometheus_pacman_exporter/cli.py
diff options
context:
space:
mode:
Diffstat (limited to 'prometheus_pacman_exporter/cli.py')
-rw-r--r--prometheus_pacman_exporter/cli.py161
1 files changed, 161 insertions, 0 deletions
diff --git a/prometheus_pacman_exporter/cli.py b/prometheus_pacman_exporter/cli.py
new file mode 100644
index 0000000..5d91b05
--- /dev/null
+++ b/prometheus_pacman_exporter/cli.py
@@ -0,0 +1,161 @@
+# SPDX-FileCopyrightText: 2026 Dennis Fink <me+coding@dennisfink.me>
+#
+# SPDX-License-Identifier: BSD-3-Clause
+
+"""Define Click parameter types and styled terminal message helpers."""
+
+from collections.abc import Callable
+from functools import update_wrapper
+from typing import Any, Concatenate
+
+import click_extra as click
+
+
+class FlexibleColorOption(click.ColorOption):
+ """Allow the color option value to be passed with or without ``=``."""
+
+ _gnu_optional_value = False
+
+
+def pass_obj_silent[T, **P, R](
+ f: Callable[Concatenate[T | None, P], R],
+) -> Callable[P, R]:
+ """Pass the current context object to a callback, if available.
+
+ The current Click context is retrieved silently. If no context exists or
+ its object is not a dictionary, ``None`` is passed instead.
+
+ The context object is inserted as the first positional argument to *f*.
+ """
+
+ def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
+ ctx = click.get_current_context(silent=True)
+ obj = ctx.obj if ctx is not None else None
+ return f(obj, *args, **kwargs)
+
+ return update_wrapper(new_func, f)
+
+
+def emit(
+ prefix: str, color: str, *message: str, enabled: bool = True, err: bool = False
+) -> None:
+ """Render a styled prefix and message to stdout.
+
+ This helper is used by :func:`error`, :func:`msg`, :func:`warn`,
+ :func:`verbose`, and :func:`debug`. The first message argument is rendered in
+ bold and subsequent arguments are appended unstyled.
+
+ :param prefix: Prefix displayed before the message.
+ :param color: Click-compatible color name applied to the prefix.
+ :param message: One or more message parts to display.
+ :param enabled: Whether the message should be emitted.
+ :param err: Write to `stderr` instead of `stdout`.
+ :raises TypeError: If no message arguments are provided.
+ """
+ if not message:
+ raise TypeError("emit() missing 1 required positional argument: 'message'")
+
+ if not enabled:
+ return
+
+ click.echo(
+ " ".join(
+ [
+ click.style(prefix, fg=color, bold=True),
+ click.style(message[0], bold=True),
+ *message[1:],
+ ]
+ ),
+ err=err,
+ )
+
+
+def error(*message: str) -> None:
+ """Print a formatted error message to stdout in red.
+
+ The first argument is rendered in bold and subsequent arguments are appended
+ unstyled. Error messages are always printed regardless of quiet or verbose
+ flags.
+
+ :param message: One or more message parts to display.
+ """
+ emit("==> ERROR:", "red", *message, err=True)
+
+
+@pass_obj_silent
+def msg(obj: dict[str, Any] | None, *message: str, err: bool = False) -> None:
+ """Print a formatted informational message to stdout in green.
+
+ The message is suppressed when the quiet flag is set. When called outside a
+ Click context, the message is emitted normally. The first argument is rendered
+ in bold and subsequent arguments are appended unstyled.
+
+ :param message: One or more message parts to display.
+ """
+ emit(
+ "==>",
+ "green",
+ *message,
+ enabled=obj is None or not obj.get("quiet", False),
+ err=err,
+ )
+
+
+@pass_obj_silent
+def warn(obj: dict[str, Any] | None, *message: str, err: bool = False) -> None:
+ """Print a formatted warning message to stdout in yellow.
+
+ The message is suppressed when the quiet flag is set. When called outside a
+ Click context, the message is emitted normally. The first argument is rendered
+ in bold and subsequent arguments are appended unstyled.
+
+ :param message: One or more message parts to display.
+ """
+ emit(
+ "==>",
+ "yellow",
+ *message,
+ enabled=obj is None or not obj.get("quiet", False),
+ err=err,
+ )
+
+
+@pass_obj_silent
+def verbose(obj: dict[str, Any] | None, *message: str, err: bool = False) -> None:
+ """Print a formatted verbose message to stdout in blue.
+
+ The message is printed only when called from a Click context with the verbose
+ flag set and the quiet flag unset. Calls made outside a Click context are a
+ no-op.
+
+ :param message: One or more message parts to display.
+ """
+ emit(
+ "==>",
+ "blue",
+ *message,
+ enabled=(
+ obj is not None
+ and obj.get("verbose", False)
+ and not obj.get("quiet", False)
+ ),
+ err=err,
+ )
+
+
+@pass_obj_silent
+def debug(obj: dict[str, Any] | None, *message: str, err: bool = False) -> None:
+ """Print a formatted debug message to stdout in magenta.
+
+ The message is printed only when called from a Click context with the debug
+ flag enabled. Calls made outside a Click context are a no-op.
+
+ :param message: One or more message parts to display.
+ """
+ emit(
+ "==>",
+ "magenta",
+ *message,
+ enabled=obj is not None and obj.get("debug", False),
+ err=err,
+ )