# Extending Kurier

Kurier is designed with extensibility at its core. If you need a transport that isn't built-in (like Redis, NATS, or SQS), you can implement it as a custom **Line**.

## Table of Contents

- [1. Subclassing `BaseKurier`](#1-subclassing-basekurier)
- [2. Implementing the Consumer](#2-implementing-the-consumer)
- [3. Registering Your Transport](#3-registering-your-transport)
- [Contribution Guidelines](#contribution-guidelines)

---

## 1. Subclassing `BaseKurier`

Every transport must inherit from `BaseKurier`. Here is the skeletal structure of a custom line:

```python
from typing import Any
from kurier.core.basekurier import BaseKurier
from kurier.core.bus_types import BusType

class MyCustomLine(BaseKurier):
    # (Optional) Assign a BusType if appropriate
    bus_type = BusType.IN_MEMORY 

    def __init__(self, name: str, config: Any, serializer: Serializer | None = None):
        super().__init__(name, serializer=serializer)
        self._config = config
        # 1. Setup your transport connection (e.g., redis.connect())
        # 2. Register yourself at the end
        self._register()

    async def _publish_to_bus(self, json_message: str, meta_data: dict[str, Any]):
        # Implementation to send the string payload to your broker.
        pass

    def _run_consumer_loop(self):
        # The internal loop that listens for messages.
        # Should call Station.dispatch(self, raw_payload).
        pass

    def close(self):
        # Graceful shutdown of connections.
        pass

    @classmethod
    def start_consumer(cls, name: str) -> bool:
        # Logic to launch the consumer loop in a background thread or task.
        pass
```

---

## 2. Implementing the Consumer

The most critical part of a custom Line is the **Consumer Loop**.

1.  **Thread vs Task**: For non-blocking I/O clients (like `aioredis`), you can run the consumer as an `asyncio.Task`. For blocking clients (like `pika` or `redis-py`), it **must** run in a dedicated `threading.Thread`.
2.  **Dispatching**: When a message arrives, call `await Station.dispatch(self, raw_data)`. The `Station` will handle the envelope decoding and handler invocation.
3.  **Error Handling**: The consumer should be resilient to connection drops and attempt to reconnect without crashing the main process.

---

## 3. Registering Your Transport

To make your transport available to the `Configurator`, you must register it:

```python
from kurier.configurations.configurator import Configurator

Configurator.register_transport(
    name="my_custom_bus", 
    class_or_path=MyCustomLine, 
    config_class=MyCustomConfigClass
)
```

Now, anyone using Kurier can define a `"my_custom_bus"` section in their `kurier.yaml` or `kurier.json`.

---

## Contribution Guidelines

We love community contributions! If you've built a high-quality transport:

1.  **Follow the Pattern**: Look at `RabbitMQLine` or `KafkaLine` for implementation patterns (e.g., handling thread-safe callbacks).
2.  **Test Thoroughly**: Provide integration tests (ideally using Testcontainers).
3.  **Add to Registry**: Update the `_register_default_transports` function in `Configurator` to include your new Line.
4.  **Submit a PR**: We'll review it and get it merged!
