# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import json


def read(log_f, raise_on_error=False):
    """Return a generator that will return the entries in a structured log file.
    Note that the caller must not close the file whilst the generator is still
    in use.

    :param log_f: file-like object containing the raw log entries, one per line
    :param raise_on_error: boolean indicating whether ValueError should be raised
                           for lines that cannot be decoded."""
    while True:
        line = log_f.readline()
        if not line:
            # This allows log_f to be a stream like stdout
            break
        try:
            yield json.loads(line)
        except ValueError:
            if raise_on_error:
                raise


def imap_log(log_iter, action_map):
    """Create an iterator that will invoke a callback per action for each item in a
    iterable containing structured log entries

    :param log_iter: Iterator returning structured log entries
    :param action_map: Dictionary mapping action name to callback function. Log items
                       with actions not in this dictionary will be skipped.
    """
    for item in log_iter:
        if item["action"] in action_map:
            yield action_map[item["action"]](item)


def each_log(log_iter, action_map):
    """Call a callback for each item in an iterable containing structured
    log entries

    :param log_iter: Iterator returning structured log entries
    :param action_map: Dictionary mapping action name to callback function. Log items
                       with actions not in this dictionary will be skipped.
    """
    for item in log_iter:
        if item["action"] in action_map:
            action_map[item["action"]](item)


class LogHandler:
    """Base class for objects that act as log handlers. A handler is a callable
    that takes a log entry as the only argument.

    Subclasses are expected to provide a method for each action type they
    wish to handle, each taking a single argument for the test data.
    For example a trivial subclass that just produces the id of each test as
    it starts might be::

      class StartIdHandler(LogHandler):
          def test_start(data):
              # For simplicity in the example pretend the id is always a string
              return data["test"]
    """

    def __call__(self, data):
        if hasattr(self, data["action"]):
            handler = getattr(self, data["action"])
            return handler(data)


def handle_log(log_iter, handler):
    """Call a handler for each item in a log, discarding the return value"""
    for item in log_iter:
        handler(item)
