# Path: src/holocron/_processors/_misc.py
# class parameters:
#     def __init__(self, *, fallback=None, jsonschema=None):
#         self._fallback = fallback or {}
#         self._jsonschema = jsonschema
# 
#     def __call__(self, fn):
#         @functools.wraps(fn)
#         def wrapper(app, *args, **kwargs):
#             signature = inspect.signature(fn)
#             arguments = signature.bind_partial(app, *args, **kwargs).arguments
# 
#             # First two arguments always are an application instance and a
#             # stream of items to process. Since they are passed by Holocron
#             # core as positional arguments, there's no real need to check their
#             # schema, so we strip them away.
#             arguments = dict(list(arguments.items())[2:])
#             parameters = dict(list(signature.parameters.items())[2:])
# 
#             # If some parameter has not been passed, a value from a fallback
#             # must be used instead (if any).
#             for param in parameters:
#                 if param not in arguments:
#                     try:
#                         value = resolve_json_references(
#                             {"$ref": self._fallback[param]},
#                             {"metadata:": app.metadata},
#                         )
#                     except (jsonpointer.JsonPointerException, KeyError):
#                         continue
# 
#                     # We need to save resolved value in both arguments and
#                     # kwargs mappings, because the former is used to *validate*
#                     # passed arguments, and the latter to supply a value from a
#                     # fallback.
#                     arguments[param] = kwargs[param] = value
# 
#             if self._jsonschema:
#                 try:
#                     format_checker = jsonschema.FormatChecker()
# 
#                     @format_checker.checks("encoding", (LookupError,))
#                     def is_encoding(value):
#                         if isinstance(value, str):
#                             import codecs
# 
#                             return codecs.lookup(value)
# 
#                     @format_checker.checks("timezone", ())
#                     def is_timezone(value):
#                         if isinstance(value, str):
#                             import dateutil.tz
# 
#                             return dateutil.tz.gettz(value)
# 
#                     @format_checker.checks("path", (TypeError,))
#                     def is_path(value):
#                         if isinstance(value, str):
#                             import pathlib
# 
#                             return pathlib.Path(value)
# 
#                     jsonschema.validate(
#                         arguments,
#                         self._jsonschema,
#                         format_checker=format_checker,
#                     )
#                 except jsonschema.exceptions.ValidationError as exc:
#                     message = exc.message
# 
#                     if exc.absolute_path:
#                         message = f"{'.'.join(exc.absolute_path)}: {exc.message}"
# 
#                     raise ValueError(message)
# 
#             return fn(app, *args, **kwargs)
# 
#         return wrapper


import collections
import pathlib
import re
import jinja2
from ._misc import parameters




def _re_match(value, pattern, flags=0):
    # If a regular expression is used agains a Python's path class, we can cast
    # the path object to string for user, because it's a behaviour a user would
    # expect anyway.
    if isinstance(value, pathlib.PurePath):
        value = str(value)
    return re.match(pattern, value, flags)


class _ConditionEvaluator:
    """Evaluate a python-like expressions in boolean context."""

    def __init__(self):
        # When processor requires some means to evaluate string conditions.
        # Turns out there're not so many simple and safe solutions, so we
        # decided to go same approach Ansible went and use Jinja2 to evaluate
        # expressions. It's safe, fast, extensible, and we already have
        # dependency on Jinja2.
        self._env = jinja2.Environment()
        self._env.filters.update({"match": _re_match})

    def eval(self, cond, **context):
        template = self._env.from_string(f"{{% if {cond} %}}true{{% endif %}}")
        return template.render(**context) == "true"



# Path: src/holocron/_processors/_misc.py
# class parameters:
#     def __init__(self, *, fallback=None, jsonschema=None):
#         self._fallback = fallback or {}
#         self._jsonschema = jsonschema
# 
#     def __call__(self, fn):
#         @functools.wraps(fn)
#         def wrapper(app, *args, **kwargs):
#             signature = inspect.signature(fn)
#             arguments = signature.bind_partial(app, *args, **kwargs).arguments
# 
#             # First two arguments always are an application instance and a
#             # stream of items to process. Since they are passed by Holocron
#             # core as positional arguments, there's no real need to check their
#             # schema, so we strip them away.
#             arguments = dict(list(arguments.items())[2:])
#             parameters = dict(list(signature.parameters.items())[2:])
# 
#             # If some parameter has not been passed, a value from a fallback
#             # must be used instead (if any).
#             for param in parameters:
#                 if param not in arguments:
#                     try:
#                         value = resolve_json_references(
#                             {"$ref": self._fallback[param]},
#                             {"metadata:": app.metadata},
#                         )
#                     except (jsonpointer.JsonPointerException, KeyError):
#                         continue
# 
#                     # We need to save resolved value in both arguments and
#                     # kwargs mappings, because the former is used to *validate*
#                     # passed arguments, and the latter to supply a value from a
#                     # fallback.
#                     arguments[param] = kwargs[param] = value
# 
#             if self._jsonschema:
#                 try:
#                     format_checker = jsonschema.FormatChecker()
# 
#                     @format_checker.checks("encoding", (LookupError,))
#                     def is_encoding(value):
#                         if isinstance(value, str):
#                             import codecs
# 
#                             return codecs.lookup(value)
# 
#                     @format_checker.checks("timezone", ())
#                     def is_timezone(value):
#                         if isinstance(value, str):
#                             import dateutil.tz
# 
#                             return dateutil.tz.gettz(value)
# 
#                     @format_checker.checks("path", (TypeError,))
#                     def is_path(value):
#                         if isinstance(value, str):
#                             import pathlib
# 
#                             return pathlib.Path(value)
# 
#                     jsonschema.validate(
#                         arguments,
#                         self._jsonschema,
#                         format_checker=format_checker,
#                     )
#                 except jsonschema.exceptions.ValidationError as exc:
#                     message = exc.message
# 
#                     if exc.absolute_path:
#                         message = f"{'.'.join(exc.absolute_path)}: {exc.message}"
# 
#                     raise ValueError(message)
# 
#             return fn(app, *args, **kwargs)
# 
#         return wrapper


# Path: src/holocron/_processors/when.py
import collections
import pathlib
import re
import jinja2
from ._misc import parameters





def _re_match(value, pattern, flags=0):
    # If a regular expression is used agains a Python's path class, we can cast
    # the path object to string for user, because it's a behaviour a user would
    # expect anyway.
    if isinstance(value, pathlib.PurePath):
        value = str(value)
    return re.match(pattern, value, flags)


class _ConditionEvaluator:
    """Evaluate a python-like expressions in boolean context."""

    def __init__(self):
        # When processor requires some means to evaluate string conditions.
        # Turns out there're not so many simple and safe solutions, so we
        # decided to go same approach Ansible went and use Jinja2 to evaluate
        # expressions. It's safe, fast, extensible, and we already have
        # dependency on Jinja2.
        self._env = jinja2.Environment()
        self._env.filters.update({"match": _re_match})

    def eval(self, cond, **context):
        template = self._env.from_string(f"{{% if {cond} %}}true{{% endif %}}")
        return template.render(**context) == "true"



@parameters(