Metadata-Version: 2.4
Name: vcti-deck
Version: 1.0.1
Summary: Generic ordered collection with named items and policy-based compatibility
Author: Visual Collaboration Technologies Inc.
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Provides-Extra: lint
Requires-Dist: ruff; extra == "lint"
Provides-Extra: typecheck
Requires-Dist: pyright; extra == "typecheck"
Dynamic: license-file

# Deck

Generic ordered collection with named items and policy-based compatibility
for Python.

## Purpose

VCollab applications need ordered collections where items are referenced
by name, support custom ordering, and optionally enforce compatibility
rules. `Deck` provides this as a lightweight, zero-dependency building
block.

## Installation

```bash
pip install vcti-deck
```

## Quick Start

### Basic Deck

```python
from vcti.deck import Deck

deck = Deck[str]()
deck.add("header", "Welcome")
deck.add("body", "Content")
deck.add("footer", "Copyright")

deck.get("header")    # "Welcome"
deck.has("body")      # True
"footer" in deck      # True
len(deck)             # 3
list(deck)            # ["Welcome", "Content", "Copyright"]
```

### Custom Ordering

```python
deck.set_order(["footer", "header"])
list(deck)        # ["Copyright", "Welcome"] — body skipped
deck.ids()        # ["footer", "header"]

deck.clear_order()
list(deck)        # ["Welcome", "Content", "Copyright"] — insertion order
```

### PolicyBoundDeck

Use `PolicyBoundDeck` when items must satisfy compatibility rules:

```python
from enum import StrEnum, auto
from vcti.deck import PolicyBoundDeck, CompatibilityPolicy

class LayerKind(StrEnum):
    IMAGE = auto()
    VECTOR = auto()

class Layer:
    def __init__(self, kind: LayerKind):
        self.kind = kind

class SameKindPolicy(CompatibilityPolicy[LayerKind, Layer]):
    def is_compatible(self, item: Layer, deck_type: LayerKind) -> bool:
        return item.kind == deck_type

image_deck = PolicyBoundDeck(deck_type=LayerKind.IMAGE, policy=SameKindPolicy())
image_deck.add("photo", Layer(LayerKind.IMAGE))     # OK
image_deck.add("lines", Layer(LayerKind.VECTOR))    # Raises ValueError
```

### Subclass Compatibility

For simpler cases, override `_is_compatible` directly:

```python
from vcti.deck import Deck

class PositiveOnlyDeck(Deck[int]):
    def _is_compatible(self, item: int) -> bool:
        return item > 0

deck = PositiveOnlyDeck()
deck.add("a", 5)    # OK
deck.add("b", -1)   # Raises ValueError
```

## API Summary

### Deck[ItemT]

| Method | Description |
|--------|-------------|
| `add(id, item, *, replace=False)` | Add item; raise if duplicate unless replace=True |
| `get(id)` | Return item or None |
| `has(id)` | Check if ID exists |
| `remove(id)` | Remove item; raise if missing |
| `set_order(ids)` | Set custom iteration order |
| `clear_order()` | Reset to insertion order |
| `ids()` | IDs in effective order |
| `items()` | Items in effective order |

### PolicyBoundDeck[DeckTypeT, ItemT]

Extends `Deck` with:

| Attribute/Method | Description |
|-----------------|-------------|
| `deck_type` | The type/category this deck is bound to |
| `policy` | The `CompatibilityPolicy` instance |

### CompatibilityPolicy[DeckTypeT, ItemT]

| Method | Description |
|--------|-------------|
| `is_compatible(item, deck_type)` | Return True if item is allowed |

## Dependencies

None. Standard library only.
