# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/04_enum.ipynb.

# %% auto 0
__all__ = ['Species', 'LoaderState']

# %% ../nbs/04_enum.ipynb 6
import io
from enum import StrEnum, auto
from contextlib import redirect_stdout, redirect_stderr
from importlib.machinery import ModuleSpec

# %% ../nbs/04_enum.ipynb 8
from types import ModuleType
from typing import (Any, Union, Optional, Iterable,)

# %% ../nbs/04_enum.ipynb 10
#| export

# %% ../nbs/04_enum.ipynb 12
#| export

# %% ../nbs/04_enum.ipynb 14
from .cons import NIL, VALUE, LOADER_STATE, _DEFAULT_, __SPEC__

# %% ../nbs/04_enum.ipynb 17
class Species(StrEnum):
    '''Cheeky enum to represent the "species" (i.e. flavor) of a duck-typed protocol.'''
    REG = auto()
    NOT = auto()
    OPT = auto()
    
    @classmethod
    def contains(cls, item: Any, items: Iterable, kind: 'Species' = 'reg') -> bool:
        kind = cls(kind)
        match kind:
            case cls.REG: return item in items
            case cls.NOT: return item not in items
            case cls.OPT: return True
            case _: return False

# %% ../nbs/04_enum.ipynb 23
class LoaderState(StrEnum):
    '''The state of the module loader.

    Attributes
    ----------
    NONE : auto
        Indicates no state.
    EXECED : auto
        Indicates the module has been executed.
    DUCKED : auto
        Indicates the module has been duck-typed.
    UNKNOWN : auto
        Indicates an unknown state.

    Methods
    -------
    _missing_(value: object) -> 'LoaderState'
        Provides a default for missing values.
    spec(module: ModuleType) -> Optional[ModuleSpec]
        Retrieves the module spec.
    get(module: ModuleType) -> 'LoaderState'
        Retrieves the loader state for a module.
    set(module: ModuleType, state: Optional[Union['LoaderState', str]] = None) -> ModuleType
        Sets the loader state for a module.
    exec(module: ModuleType) -> ModuleType
        Executes the module and sets its state to DUCKED.
    '''
    NONE = auto()
    EXECED = auto()
    DUCKED = auto()
    UNKNOWN = auto()
    
    @classmethod
    def _missing_(cls, value: object) -> 'LoaderState':
        if value is None: return cls.NONE
        return cls.UNKNOWN 
                
    @classmethod
    def spec(cls, module: ModuleType) -> Optional[ModuleSpec]:
        try: return object.__getattribute__(module, __SPEC__)
        except: return None
        
    @classmethod
    def get(cls, module: ModuleType) -> 'LoaderState':
        try: return cls(cls.spec(module).loader_state)
        except: return cls.UNKNOWN

    @classmethod
    def set(cls, module: ModuleType, state: Optional[Union['LoaderState', str]] = None) -> ModuleType:
        try: setattr(cls.spec(module), LOADER_STATE, str(cls(state)))
        except: ...
        return module
        
    @classmethod
    def exec(cls, module: ModuleType) -> ModuleType:
        spec = cls.spec(module)
        with redirect_stdout(io.StringIO()), redirect_stderr(io.StringIO()):
            spec.loader.exec_module(module)
        return cls.set(module, cls.DUCKED)
