# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 smartindent nu fileencoding=utf-8
from __future__ import unicode_literals
"""
ODYLDo - Ordered Dict Yaml Loader / Dumper
------------------------------------------

a simple implementation of a Ordered Dict Loader for PyYAML;
because chaos is fun but order matters on loading dicts from a yaml file;

(c) 2014 - 2020 Klaus Zerwes zero-sys.net
This package is free software.
This software is licensed under the terms of the
GNU GENERAL PUBLIC LICENSE version 3 or later,
as published by the Free Software Foundation.
See https://www.gnu.org/licenses/gpl.html
"""

try:
    from collections import OrderedDict
except ImportError:
    # try importing the backported replacement
    # requires a: `pip-2.6 install ordereddict`
    from ordereddict import OrderedDict 

import yaml
import yaml.loader
import yaml.dumper
import yaml.representer

# @see: yaml.resolver.DEFAULT_MAPPING_TAG
ODYLDoYAMLMAPS = [
    'tag:yaml.org,2002:map',
    'tag:yaml.org,2002:omap',
    ]

class ODYL(yaml.SafeLoader):
    """Ordered Dict Yaml Loader"""
    def __init__(self, *args, **kwargs):
        yaml.SafeLoader.__init__(self, *args, **kwargs)
        for mapping in ODYLDoYAMLMAPS:
            self.add_constructor(mapping, type(self)._odyload)

    # see pyyaml constructors construct_*
    def _odyload(self, node):
        if node.tag not in ODYLDoYAMLMAPS:
            raise Exception('called ODYLoad for unregistered mapping mode "%s" aka. "%s"' % (node.tag, node.id,))
        data = OrderedDict()
        yield data
        data.update(self.construct_mapping(node))

    # see pyyaml construct_mapping
    def construct_mapping(self, node, deep=False):
        self.flatten_mapping(node)
        m = OrderedDict()
        for k, v in node.value:
            m[self.construct_object(k, deep=deep)] = self.construct_object(v, deep=deep)
        return m

class ODYD(yaml.SafeDumper):
    """Ordered Dict Yaml Dumper"""
    def __init__(self, *args, **kwargs):
        yaml.SafeDumper.__init__(self, *args, **kwargs)
        yaml.representer.SafeRepresenter.add_representer(OrderedDict, type(self)._odyrepr)
    def _odyrepr(self, data):
        """see: yaml.representer.represent_mapping"""
        return self.represent_mapping('tag:yaml.org,2002:map', data.items())

def safe_load(stream):
    """implementation of safe loader using Ordered Dict Yaml Loader"""
    return yaml.load(stream, ODYL)

def safe_load_all(stream):
    """safe parse all YAML documents in a stream using Ordered Dict Yaml Loader"""
    return yaml.load_all(stream, ODYL)

def safe_dump(data, stream=None, **kwds):
    """implementation of safe dumper using Ordered Dict Yaml Dumper"""
    return yaml.dump(data, stream=stream, Dumper=ODYD, **kwds)