```yaml
Author: Price Hiller
Contributors:
    - Jacob B. Sanders
Description: A python library for emitting and reading events asynchronously.
Type: Library
License: BSD 3-Clause License
Copyright: 2021, Price Hiller
```

# Async Events

A(n) OOP asynchronous Python library capable of emitting custom events based on string matching.

## Table Of Contents

- [Async Events](#async-events)
    - [Table Of Contents](#table-of-contents)
    - [Installation](#installation)
    - [Usage](#usage)
        - [Basic Usage](#basic-usage)
        - [Advanced Usage](#advanced-usage)
            - [Event Types](#event-types)
            - [Custom Event Listeners](#custom-event-listeners)

## Installation
- From pip
```bash
python3 -m pip install avents
```

## Installation from Source ##

```bash
python[3] setup.py install
```

## Usage

### Basic Usage

Using the `listen` decorator you can define a function to listen for an event like so and attach a parser for the events:
```python
from avents import listen # the listener that matches for a string via `listen("some string")`
from avents import Event # an event object defining a name of the event and the event content
from avents import parse # the parser required to emit events via `await parse(Event("some event", "event content")

# The listener
@listen("event", "another event") # A listener can listen to multiple events
async def example_event_function(event: Event):
    print(f"Received event: \"{event.name}\", with content: \"{event.content}\"")

# The parser
async def parser():
    # The actual parsing of an event below
    await parse(Event(name="event", content="example event content"))
    await parse(Event(name="another event", content="another event for the same handler"))
```

A full, runnable, example:

```python
import asyncio # Required to run the application

# avents imports
from avents import listen # the listener that matches for a string via `listen("some string")`
from avents import Event # an event object defining a name of the event and the event content
from avents import parse # the parser required to emit events via `await parse(Event("some event", "event content")

# The listener
@listen("event")
async def your_event(event: Event):
    print(f"Received event: \"{event.name}\", with content: \"{event.content}\"")

async def main():
    # The actual parsing of an event below
    await parse(Event(name="event", content="this is some event content"))


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
```

### Advanced Usage
#### Event Types
Instead of using raw strings in the `listen` decorator, for larger projects it may become much more practical to use a
class container of events to listen for.

Avents provides a base event type class to inherit from, that being `BaseEventType` which is an enum object.

To define event type enums inherit from the said base class:

```python
from avents import BaseEventType

class CustomEventType(BaseEventType):
    SOME_EVENT: str = "an event"
```

This can then be used in conjunction with the `listen` decorator to maintain consistency throughout a larger project
like so:

```python
from avents import BaseEventType
from avents import listen
from avents import Event

class CustomEventType(BaseEventType):
    SOME_EVENT: str = "an event"

@listen(CustomEventType.SOME_EVENT)
async def some_event(event: Event):
    print(event)
```

#### Custom Event Listeners
In the scenario in which you may want to parse a base event and then pass it to further sub event parsing creating a 
custom event listener can save quite the headache.

In a basic use case we only used the `EventListener` as provided to us by avents which is exposed by `listen` and 
`parse` in more simplistic use cases.

With the custom listener inheriting the base `EventListener` exposes custom listening:

```python
from avents import EventListener

class CustomEventListener(EventListener):
    ...
```

Or you can include a constructor as well:

```python
from avents import EventListener

class CustomEventListener(EventListener):
    
    def __init__(self):
        super().__init__()
```

Once a custom event listener has been created the `listen` and `parse` methods are immediately available:

```python
from avents import EventListener
from avents import Event

class CustomEventListener(EventListener):
    ...

async def example_event_emitter():
    for i in range(2):
        await CustomEventListener.parse(Event(name=str(i), content=i))

@CustomEventListener.listen("0", "1", "2") # Listen() can take multiple events to listen for
async def listen_for_one(event: Event):
    print(event)
```

It can be extended further by incorporating the `EventListener` class as the base event handler in the following
pattern:

```python
from avents import EventListener
from avents import Event

class CustomEventListener(EventListener):
    ...

async def example_event_emitter():
    for i in range(2):
        await EventListener.parse(Event(name=str(i), content=i))

@EventListener.listen("0")      
async def base_event_handler(event: Event):
    await CustomEventListener.parse(Event(name=event.content + 1, 
                                          content="We incremented to generate a design pattern")
                                    )

@CustomEventListener.listen("1")
async def listen_for_one(event: Event):
    print(event)
```

Notice that we use the base `EventListener` for the initial parsing of an emitted event by `example_event_emitter`
which will then be passed to the `base_event_handler` when an event of name `0` is emitted which then does subparsing
for the `CustomEventListener` class.

This can be extended to create entire command structures or other such hierarchical structures.
