# Serialization & CloudEvents

Kurier uses a robust serialization system to transform Python objects into wire-ready payloads and back again. This guide explains how to use **Pydantic**, **Dataclasses**, and the **CloudEvents** standard.

## Table of Contents

- [1. Supported Message Types](#1-supported-message-types)
- [2. The CloudEvents Standard](#2-the-cloudevents-standard)
- [3. Custom Serializers](#3-custom-serializers)
- [Serialization Lifecycle](#serialization-lifecycle)

---

## 1. Supported Message Types

Kurier's default serializer is built for modern Python. When you publish a message, Kurier automatically extracts the data from:

*   **Pydantic Models**: (Recommended) Uses `model_dump()` to extract data. Supports Pydantic v2 recursively.
*   **Dataclasses**: Uses `asdict()` for clean serialization.
*   **Built-in Types**: Strings, integers, floats, booleans, and dictionaries are supported directly.
*   **Nested Datetimes**: Kurier automatically converts `datetime.datetime` objects to **ISO 8601** strings during the serialization process.

---

## 2. The CloudEvents Standard

Kurier includes built-in support for the **CloudEvents** specification. This is ideal for cross-platform event-driven architectures where your events may be consumed by other services (like Azure Event Grid or AWS EventBridge).

### Using the CloudEvents Serializer
You can enable CloudEvents for a specific Line by assigning the `CloudEventSerializer` during initialization or via your configuration file.

```python
from kurier.transports.inmemory.bus import InMemoryLine
from kurier.utilities.serializers import CloudEventSerializer

line = InMemoryLine(
    name="events", 
    serializer=CloudEventSerializer(source="/my-app/orders")
)
```

### Generated Envelope
A message published via the `CloudEventSerializer` will look like this on the wire:

```json
{
  "specversion": "1.0",
  "id": "019578a6-4a59-7e3e-b0a8-6c0d6f7a9b12",
  "source": "/my-app/orders",
  "type": "myapp.events.OrderCreated",
  "time": "2024-03-08T12:00:00Z",
  "datacontenttype": "application/json",
  "data": {
    "order_id": "12345",
    "amount": 99.99
  },
  "correlationid": "019578a6-4a59-7e3e-b0a8-6c0d6f7a9b12",
  "metroobjecttypename": "OrderCreated",
  "metroqualifiedobjecttypename": "myapp.events.OrderCreated"
}
```

Kurier stores its own routing metadata in CloudEvents extension attributes so `Station` can continue performing type-based dispatch after deserialization.
The `metro*` extension attribute names are preserved for wire compatibility with previously published events.

---

## 3. Custom Serializers

Need to support Protobuf, XML, or custom encryption? You can implement your own serializer by subclassing `Serializer`.

```python
from kurier.utilities.serializers import Serializer
from kurier.core.kurier_message import KurierMessage

class MyCustomSerializer(Serializer):
    def serialize(self, data: Any) -> str | bytes:
        # Transform data to your wire format
        pass

    def deserialize(self, raw_data: str | bytes) -> Any:
        # Reconstruct Python data
        pass

    def serialize_envelope(self, envelope: KurierMessage) -> str | bytes:
        # Optional: Customize the entire envelope structure
        pass
```

---

## Serialization Lifecycle

1.  **Object Conversion**: Your `OrderCreated` object is converted into a standard Python `dict`.
2.  **Date/Time Handling**: Any `datetime` objects in the dict are converted to strings.
3.  **Envelope Wrapping**: The data is placed inside a `KurierMessage` envelope.
4.  **Wire Serialization**: The entire envelope is passed to the Serializer's `serialize_envelope()` method.
5.  **Transit**: The serialized string/bytes are sent across the "Line" to the broker.
