Metadata-Version: 2.4
Name: baobab-tcg-core
Version: 0.5.0
Summary: Socle Python générique pour les librairies TCG Baobab.
Project-URL: Homepage, https://github.com/baobabgit/baobab-tcg-core
Project-URL: Repository, https://github.com/baobabgit/baobab-tcg-core
Project-URL: Issues, https://github.com/baobabgit/baobab-tcg-core/issues
Author: Patrick ANDRIANAIVO
License: MIT License
        
        Copyright (c) 2026 Michel ANDRIANAIVO
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: baobab,domain-model,tcg,trading-card-game
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.11
Provides-Extra: dev
Requires-Dist: bandit[sarif,toml]>=1.7; extra == 'dev'
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pip-audit>=2.7; extra == 'dev'
Requires-Dist: pre-commit>=3.8; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: furo>=2024.0; extra == 'docs'
Requires-Dist: sphinx>=7.0; extra == 'docs'
Description-Content-Type: text/markdown

# baobab-tcg-core

[![CI](https://github.com/baobabgit/baobab-tcg-core/actions/workflows/ci.yml/badge.svg)](https://github.com/baobabgit/baobab-tcg-core/actions/workflows/ci.yml)
[![Python versions](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org)

Librairie Python métier pure pour partager le langage commun des Trading Card
Games dans l'écosystème Baobab.

## Table des matières

- [À propos](#à-propos)
- [Fonctionnalités](#fonctionnalités)
- [Hors périmètre](#hors-périmètre)
- [Stack technique](#stack-technique)
- [Architecture](#architecture)
- [Structure du projet](#structure-du-projet)
- [Installation](#installation)
- [Démarrage rapide](#démarrage-rapide)
- [Exemples d'utilisation](#exemples-dutilisation)
- [API publique](#api-publique)
- [Configuration](#configuration)
- [Qualité](#qualité)
- [Sécurité](#sécurité)
- [Workflow](#workflow)
- [Roadmap](#roadmap)
- [Contribuer](#contribuer)
- [Licence](#licence)
- [Auteur](#auteur)

## À propos

`baobab-tcg-core` fournit les types métier génériques utilisés par les futures
librairies `baobab-tcg-*` : identifiants, références, objets de valeur,
énumérations, erreurs, protocoles et helpers de sérialisation.

La librairie reste neutre : elle ne connaît ni les règles de Riftbound, Magic,
Pokémon, Altered, Lorcana ou tout autre TCG, ni les catalogues concrets qui les
alimentent.

## Fonctionnalités

- Identifiants génériques : `GameCode`, `SetCode`, `LanguageCode`,
  `CurrencyCode`, `Slug`, `ExternalCardId`, `ExternalPrintingId`,
  `ExternalSetId`, `PrintingCode`, `CollectorNumber`.
- Références métier : `GameReference`, `CardReference`, `PrintingReference`,
  `SetReference`.
- Objets de valeur : `Quantity`, `MoneyAmount`, `LocalizedText`, `Metadata`,
  `Tag`, `Percentage`.
- Énumérations transverses : langues, raretés, états, variantes, finitions,
  statuts d'échange et types de produits.
- Erreurs métier communes et explicites.
- Protocoles d'intégration pour les catalogues et resolvers externes.
- Sérialisation simple vers des dictionnaires JSON-safe.

## Hors périmètre

Le core ne gère pas les collections utilisateur, decks, achats, bases de données,
API HTTP, authentification, scraping, règles de deckbuilding ou règles propres à
un jeu. Ces responsabilités appartiennent à des librairies spécialisées comme
`baobab-tcg-collection`, `baobab-tcg-deck`, `baobab-tcg-database` ou aux packages
propres à un TCG.

## Stack technique

| Domaine | Outil |
| --- | --- |
| Langage | Python ≥ 3.11 |
| Environnement | `venv` (`.venv`) + `pip` |
| Build | `hatchling` + `hatch-vcs` |
| Lint & format | `ruff` |
| Typage | `mypy` strict |
| Tests | `pytest` + `pytest-cov` |
| Documentation | Sphinx + Furo, reStructuredText |
| CI / hooks | GitHub Actions, `pre-commit` |

Runtime dependencies: aucune dépendance obligatoire.

## Architecture

Le modèle est centré sur des références neutres :

```text
game_code
external_card_id
external_printing_id
set_code
```

Chaque librairie spécialisée reste responsable de ses propres catalogues, règles
et mappings. Le core fournit uniquement les contrats et objets partagés.

## Structure du projet

```text
.
├── src/baobab_tcg_core/       # Code de la librairie
├── tests/baobab_tcg_core/     # Tests en miroir
├── docs/
│   ├── specifications/        # CDC, US et FEAT
│   ├── api/                   # Documentation API autodoc
│   ├── guides/                # Tutoriels et how-to
│   └── workflow/              # Rôles, gates et handoff IA
├── pyproject.toml
├── AGENTS.md
└── .github/
```

## Installation

```bash
git clone https://github.com/baobabgit/baobab-tcg-core.git
cd baobab-tcg-core
python -m venv .venv
```

Windows PowerShell :

```powershell
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e ".[dev,docs]"
```

Linux / macOS :

```bash
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev,docs]"
```

## Démarrage rapide

```bash
ruff check .
ruff format --check .
mypy
pytest
```

## Exemples d'utilisation

### Références de cartes

```python
from baobab_tcg_core import CardReference, ExternalCardId, GameCode

reference = CardReference(
    game_code=GameCode("riftbound"),
    external_card_id=ExternalCardId("RIFTBOUND_CARD_123"),
)

assert str(reference.game_code) == "riftbound"
assert str(reference.external_card_id) == "RIFTBOUND_CARD_123"
```

### Énumérations

```python
from baobab_tcg_core import CardCondition, CardFinish, Language, Rarity

condition = CardCondition.NEAR_MINT
assert condition == "NEAR_MINT"          # stable pour sérialisation
assert CardCondition("NEAR_MINT") is CardCondition.NEAR_MINT

rarity = Rarity.RARE
language = Language.FR
finish = CardFinish.FOIL
```

### Objets de valeur avec sérialisation

```python
from decimal import Decimal
from baobab_tcg_core import MoneyAmount, Percentage, Quantity
from baobab_tcg_core.identifiers import CurrencyCode

qty = Quantity(10)
price = MoneyAmount(amount=Decimal("4.99"), currency=CurrencyCode("EUR"))
tax = Percentage(Decimal("20"))

# Roundtrip JSON-safe
assert Quantity.from_dict(qty.to_dict()) == qty
assert MoneyAmount.from_dict(price.to_dict()) == price
assert price.to_dict()["amount"] == "4.99"   # Decimal → str
```

### Protocoles de résolution

```python
from baobab_tcg_core import GameRegistry, CardReferenceResolver

class MyGameRegistry:
    def is_supported(self, game_code):
        return str(game_code) in {"magic", "pokemon"}

assert isinstance(MyGameRegistry(), GameRegistry)  # vérification structurelle
```

### Protocoles composites et `Supports*`

```python
from baobab_tcg_core import (
    ReferenceResolver,
    SupportsGameCode, SupportsCardReference,
    SupportsPrintingReference, SupportsSetReference,
)

# ReferenceResolver : composite pour un resolver multi-types
class MyCatalog:
    def exists(self, reference):
        return True  # implémentation réelle ici

assert isinstance(MyCatalog(), ReferenceResolver)

# Supports* : typer des objets qui exposent une référence
class MyCardEntry:
    @property
    def card_reference(self):
        ...

assert isinstance(MyCardEntry(), SupportsCardReference)
```

### Fakes InMemory (utilitaires de test)

```python
from baobab_tcg_core.testing import (
    InMemoryGameRegistry,
    InMemoryCardReferenceResolver,
)
from baobab_tcg_core import GameCode, CardReference, ExternalCardId

# Dans vos tests : simuler un registre sans infrastructure
registry = InMemoryGameRegistry(supported={GameCode("magic")})
assert registry.is_supported(GameCode("magic")) is True

# Simuler un resolver de cartes
ref = CardReference(game_code=GameCode("magic"), external_card_id=ExternalCardId("c001"))
resolver = InMemoryCardReferenceResolver(known={ref})
assert resolver.exists(ref) is True
```

## Protocoles d'intégration

Le core définit uniquement des **contrats** — il ne résout rien lui-même.

| Protocole | Rôle |
|---|---|
| `GameRegistry` | Vérifier si un jeu est supporté |
| `CardReferenceResolver` | Vérifier l'existence d'une `CardReference` |
| `PrintingReferenceResolver` | Vérifier l'existence d'une `PrintingReference` |
| `SetReferenceResolver` | Vérifier l'existence d'une `SetReference` |
| `ReferenceResolver` | Composite des trois resolvers |
| `SupportsGameCode` | Typer un objet qui expose un `GameCode` |
| `SupportsCardReference` | Typer un objet qui expose une `CardReference` |
| `SupportsPrintingReference` | Typer un objet qui expose une `PrintingReference` |
| `SupportsSetReference` | Typer un objet qui expose une `SetReference` |

Les méthodes `exists()` retournent un `bool` — le core ne connaît pas les modèles
riches (`Card`, `Printing`, `Set`) ; ces responsabilités appartiennent aux librairies
consommatrices (`baobab-tcg-catalog`, `baobab-tcg-collection`, etc.).

Pour tester une intégration, utilisez `baobab_tcg_core.testing` qui fournit des
implémentations InMemory documentées comme utilitaires de test uniquement.

## API publique

Les symboles exportés depuis `baobab_tcg_core.__init__` constituent le contrat
public. Une suppression ou modification incompatible d'un symbole public impose
un bump SemVer majeur, une entrée `CHANGELOG` marquée `BREAKING` et une note de
migration.

```python
from baobab_tcg_core import (
    # Erreurs
    BaobabTcgError, SerializationError, InvalidGameCodeError,
    # Identifiants
    GameCode, SetCode, LanguageCode, CurrencyCode, Slug,
    ExternalCardId, ExternalPrintingId, ExternalSetId,
    CollectorNumber, PrintingCode,
    # Références
    CardReference, GameReference, PrintingReference, SetReference,
    # Objets de valeur
    JsonValue, LocalizedText, Metadata, MoneyAmount,
    Percentage, Quantity, Tag,
    # Énumérations
    CardCondition, CardFinish, CardVariant,
    Language, ProductType, Rarity, TradeStatus,
    # Protocoles
    CardReferenceResolver, GameRegistry,
    PrintingReferenceResolver, ReferenceResolver, SetReferenceResolver,
    SupportsCardReference, SupportsGameCode,
    SupportsPrintingReference, SupportsSetReference,
)
```

## Configuration

Le core est une librairie métier pure : il ne charge pas de configuration à
l'import et ne lit pas de fichier d'environnement. Les projets hôtes injectent
leur propre configuration dans leurs couches applicatives.

## Qualité

Les gates locaux attendus sont :

```bash
ruff check .
ruff format --check .
mypy
pytest
python -m build
```

La couverture minimale est fixée à 90 % dans `pyproject.toml`.

## Sécurité

- Aucun secret dans le code ou Git.
- Pas de requête réseau ni d'accès fichier implicite dans le core.
- Validation explicite des données métier.
- Analyse `bandit` et `pip-audit` dans la CI.

Signalez toute vulnérabilité en privé via [SECURITY.md](SECURITY.md).

## Workflow

Le dépôt suit un workflow multi-IA séquentiel décrit dans `docs/workflow/` :
Product Owner, Architecte, Développeur, Relecteur, Sécurité conditionnelle puis
Release Manager. Chaque tâche porte un identifiant traçable `US → FEAT → TASK`.

## Roadmap

- `0.1.0` : socle package, erreurs, identifiants, références, value objects,
  enums, sérialisation, tests et README.
- `0.2.0` : protocoles enrichis, helpers de mapping et premiers retours
  d'intégration.
- `1.0.0` : API publique stabilisée après usage par plusieurs librairies
  consommatrices.

## Contribuer

Lisez [AGENTS.md](AGENTS.md) pour les règles de développement et
[CONTRIBUTING.md](CONTRIBUTING.md) pour le cycle de contribution.

## Licence

Distribué sous licence MIT. Voir [LICENSE](LICENSE).

## Auteur

Patrick ANDRIANAIVO.
