summaryrefslogtreecommitdiff
path: root/tests/tmp/todo.md
blob: a0c6a83c6c31ffa0cc6c4a57234ac1609d8933b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# zr_protocol - Todo V1

> Crate Rust de communication réseau : protocole custom, sérialisation binaire, proc-macro pour définir des packets via un format `.packet`.

---

## Phase 0 — Workspace & Scaffold

- [ ] **0.1** Créer le workspace virtuel `Cargo.toml` à la racine (members: `zr-protocol`, `zr-protocol-macros`)
- [ ] **0.2** Créer le crate `zr-protocol/` (library, edition 2024)
  - [ ] `Cargo.toml` avec dépendances : `bytes = "1"`, `thiserror = "2"`, features optionnelles (`tokio`, `serde`, `derive`)
  - [ ] `src/lib.rs` avec modules déclarés : `encode`, `decode`, `packet`, `codec`, `framing`, `builder`, `error`, `impls`
- [ ] **0.3** Créer le crate `zr-protocol-macros/` (proc-macro = true, edition 2024)
  - [ ] `Cargo.toml` avec dépendances : `syn = { version = "2", features = ["full"] }`, `quote = "1"`, `proc-macro2 = "1"`
  - [ ] `src/lib.rs` minimal (entry point vide)
- [ ] **0.4** Supprimer les anciens fichiers `src/bin/client.rs` et `src/bin/server.rs` ( remplacer par des exemples dans `examples/` )
- [ ] **0.5** Vérifier que `cargo check --workspace` compile sans erreur
- [ ] **0.6** Créer la structure de dossiers dans les deux crates :
  - `zr-protocol/src/encode.rs`, `decode.rs`, `packet.rs`, `codec.rs`, `framing.rs`, `builder.rs`, `error.rs`
  - `zr-protocol/src/impls/mod.rs`, `primitives.rs`, `arrays.rs`, `option.rs`
  - `zr-protocol-macros/src/parser/ast.rs`, `lexer.rs`, `grammar.rs`, `mod.rs`
  - `zr-protocol-macros/src/codegen/struct_gen.rs`, `encode_gen.rs`, `decode_gen.rs`, `registry_gen.rs`, `mod.rs`
  - `examples/login/packets/`, `examples/login/server.rs`, `examples/login/client.rs`
  - `tests/` (racine workspace)

---

## Phase 1 — Traits Core : `Encode` & `Decode`

> Les traits fondamentaux de sérialisation/déserialization binaire.

- [ ] **1.1** Définir `src/error.rs` — type d'erreur `ZrError`
  ```rust
  use thiserror::Error;
  #[derive(Debug, Error)]
  pub enum ZrError {
      #[error("io error: {0}")]
      Io(#[from] std::io::Error),
      #[error("invalid packet id: 0x{0:02X}")]
      InvalidPacketId(u32),
      #[error("buffer underflow: expected {expected} bytes, got {got}")]
      Underflow { expected: usize, got: usize },
      #[error("packet too large: {0} bytes (max {1})")]
      PacketTooLarge(usize, usize),
      #[error("unknown field type: {0}")]
      UnknownType(String),
      #[error("decode error: {0}")]
      Decode(String),
  }
  ```
- [ ] **1.2** Définir `src/encode.rs` — trait `Encode`
  ```rust
  pub trait Encode {
      fn encode(&self, buf: &mut BytesMut) -> io::Result<()>;
      fn encoded_size(&self) -> usize; // optionnel, pour pré-réserver
  }
  ```
- [ ] **1.3** Définir `src/decode.rs` — trait `Decode`
  ```rust
  pub trait Decode: Sized {
      fn decode(buf: &mut BytesMut) -> io::Result<Self>;
  }
  ```
- [ ] **1.4** Impl `Encode` pour les types primitifs dans `src/impls/primitives.rs`
  - `u8`, `u16`, `u32`, `u64`, `u128`
  - `i8`, `i16`, `i32`, `i64`, `i128`
  - `bool` (1 octet : 0x00 = false, 0x01 = true)
  - `f32`, `f64` (via `to_be_bytes()` / `to_le_bytes()`)
  - Tous en big-endian par défaut (network byte order)
- [ ] **1.5** Impl `Encode` pour `String` et `Vec<u8>`
  - Format : `u32` (longueur en octets) + octets bruts
- [ ] **1.6** Impl `Encode` pour `[u8; N]` dans `src/impls/arrays.rs` (écriture directe, pas de longueur)
- [ ] **1.7** Impl `Encode` pour `Option<T: Encode>` dans `src/impls/option.rs`
  - Format : `u8` tag (0x00 = None, 0x01 = Some) + données si Some
- [ ] **1.8** Impl `Decode` pour tous les mêmes types (mirror de 1.4 à 1.7)
  - Avec gestion propre des erreurs (underflow → `ZrError::Underflow`)
- [ ] **1.9** Impl `Encode`/`Decode` pour `Vec<T: Encode/Decode>`
  - Format : `u32` nombre d'éléments + sérialisation de chaque élément
- [ ] **1.10** Tests unitaires pour chaque type implémenté
  - Roundtrip : encode → decode → assert_eq
  - Cas limites : chaîne vide, vec vide, None, MAX values

---

## Phase 2 — Trait `PacketMeta` & Types Runtime

> Métadonnées des packets et typage dynamique.

- [ ] **2.1** Définir `src/packet.rs` — trait `PacketMeta`
  ```rust
  pub trait PacketMeta {
      const ID: u32;
      const NAME: &'static str;
      const SIZE_HINT: Option<usize>;
  }
  ```
- [ ] **2.2** Définir un enum `PacketId` (ou type alias `u32`) pour les IDs réservés
  - Réservé `0x00` = Reserved, `0xFF` = KeepAlive/Ping
- [ ] **2.3** Définir `pub struct ZrPacket` (wrapper type-érasé pour le codec)
  ```rust
  pub struct ZrPacket {
      pub id: u32,
      pub payload: BytesMut,
  }
  ```
- [ ] **2.4** Définir `pub trait IntoZrPacket: PacketMeta + Encode` pour convertir un typed packet → `ZrPacket`
- [ ] **2.5** Tests pour `PacketMeta` et `ZrPacket`

---

## Phase 3 — Format `.packet` : Lexer & Parser

> Parsing du format texte de définition de protocole.

- [ ] **3.1** Définir l'AST dans `zr-protocol-macros/src/parser/ast.rs`
  ```rust
  pub struct ProtocolFile {
      pub packets: Vec<PacketDef>,
  }
  pub struct PacketDef {
      pub attributes: Vec<Attribute>,
      pub name: Ident,
      pub id: u32,
      pub fields: Vec<FieldDef>,
  }
  pub struct FieldDef {
      pub attributes: Vec<Attribute>,
      pub name: Ident,
      pub ty: TypeRef,
  }
  pub enum TypeRef {
      Primitive(String),          // u32, String, bool, etc.
      Option(Box<TypeRef>),       // Option<T>
      Vec(Box<TypeRef>),          // Vec<T>
      Array(String, usize),       // [u8; 64]
      Custom(String),             // un autre type packet
  }
  pub struct Attribute {
      pub name: String,
      pub value: Option<String>,
  }
  ```
- [ ] **3.2** Implémenter le lexer dans `zr-protocol-macros/src/parser/lexer.rs`
  - Tokens : `Packet`, `Ident`, `Number` (hex 0x.. et décimal), `Colon`, `BraceOpen`, `BraceClose`, `BracketOpen`, `BracketClose`, `Eq`, `String`, `Comma`, `EndAttribute`, `Hash`
  - Whitespace et comments (`//`) ignorés
- [ ] **3.3** Implémenter le parser dans `zr-protocol-macros/src/parser/grammar.rs`
  - Parse un `ProtocolFile` à partir de tokens
  - Validation : nom unique, ID unique, types connus
  - Erreurs avec span (ligne/colonne) pour de bons messages d'erreur
- [ ] **3.4** Tests du parser :
  - [ ] Format valide basique
  - [ ] Attributs optionnels (`#[endian = "big"]`)
  - [ ] Types `Option<T>`, `Vec<T>`, `[u8; N]`
  - [ ] Erreur : type inconnu
  - [ ] Erreur : syntaxe invalide
  - [ ] Erreur : ID manquant
  - [ ] Fichier vide (pas de packet)

---

## Phase 4 — Proc-macro `packet!`

> Génération de code Rust à partir du format `.packet`.

- [ ] **4.1** Implémenter l'entry point `packet!` dans `zr-protocol-macros/src/lib.rs`
  - Accepte `include!("path/to/file.packet")` OU du code inline
  - Lit le fichier via `CARGO_MANIFEST_DIR` + chemin relatif
- [ ] **4.2** Codegen struct dans `zr-protocol-macros/src/codegen/struct_gen.rs`
  - Génère `#[derive(Debug, Clone)] pub struct NomPacket { pub field: Type, ... }`
  - Gère `Option<T>``Option<T>`, `Vec<T>``Vec<T>`, `[u8; N]``[u8; N]`
- [ ] **4.3** Codegen `impl PacketMeta` dans `zr-protocol-macros/src/codegen/registry_gen.rs`
  - `const ID: u32 = ...; const NAME: &'static str = "..."; const SIZE_HINT: Option<usize> = None;`
- [ ] **4.4** Codegen `impl Encode` dans `zr-protocol-macros/src/codegen/encode_gen.rs`
  - Pour chaque champ : `Encode::encode(&self.champ, buf)?;`
- [ ] **4.5** Codegen `impl Decode` dans `zr-protocol-macros/src/codegen/decode_gen.rs`
  - Pour chaque champ : `champ: Decode::decode(buf)?`
- [ ] **4.6** Codegen registry globale (un seul `match` ID → nom) dans `registry_gen.rs`
  - Fonction `pub fn packet_name_by_id(id: u32) -> Option<&'static str>`
- [ ] **4.7** Codegen `impl IntoZrPacket` pour chaque packet
  - Sérialise → `ZrPacket { id, payload }`
- [ ] **4.8** Gestion des attributs dans le codegen
  - `#[endian = "big"]` → big-endian (défaut), `#[endian = "little"]` → little-endian
  - `#[skip_if_none]` → ne sérialise pas le champ si `None` (sans le tag)
- [ ] **4.9** Tests d'intégration :
  - [ ] Un fichier `.packet` simple → compiles, encode/decode roundtrip
  - [ ] Fichier avec `Option<T>` et `Vec<T>`
  - [ ] Compilation échoue sur type inconnu (trybuild)
  - [ ] Compilation échoue sur syntaxe invalide (trybuild)
- [ ] **4.10** Macro `packet!` inline (pour les petits définitions sans fichier)
  ```rust
  packet! {
      packet Ping 0xFF {
          sequence: u32,
      }
  }
  ```

---

## Phase 5 — Builder Pattern (secondaire)

> API alternative pour construire des packets sans struct literals.

- [ ] **5.1** Définir le trait `PacketBuilder` dans `src/builder.rs`
  ```rust
  pub trait PacketBuilder: Sized {
      type Packet: Encode + PacketMeta;
      fn new() -> Self;
      fn field<T: IntoFieldValue>(mut self, name: &str, value: T) -> Self;
      fn build(self) -> io::Result<Self::Packet>;
  }
  ```
- [ ] **5.2** Codegen du builder dans `zr-protocol-macros/src/codegen/builder_gen.rs`
  - Génère une struct `NomPacketBuilder { username: Option<String>, ... }` avec chaque champ en `Option`
  - `field()` matche sur le nom (string) et set la valeur
  - `build()` vérifie que tous les champs sont présents, sinon erreur
- [ ] **5.3** Opt-out via attribute : `#[packet(no_builder)]` désactive la génération du builder
- [ ] **5.4** Tests :
  - [ ] Builder crée un packet valide
  - [ ] Builder erreur si champ manquant
  - [ ] `#[packet(no_builder)]` ne génère pas le builder

---

## Phase 6 — Feature Flags & Extensibilité

> Support optionnel de crates externes.

- [ ] **6.1** Feature `tokio` : active les dépendances `tokio` + `tokio-util`
  - Active le module `codec.rs` et `framing.rs`
- [ ] **6.2** Feature `serde` : active `serde` + `bincode`
  - Génère `#[derive(serde::Serialize, serde::Deserialize)]` sur les structs via le proc-macro
  - Attribut `#[packet(serde)]` pour forcer ou `#[packet(no_serde)]` pour désactiver par packet
- [ ] **6.3** Feature `derive` : active `zr-protocol-macros`
  - Les macros `packet!`, `include_packets!` ne sont disponibles que avec cette feature
- [ ] **6.4** Feature `std` (défaut) : support `String`, `Vec`, etc.
  - Feature `no_std` future (pas v1) : uniquement `[u8; N]`, `u8`, etc.
- [ ] **6.5** Documentation des features dans le `Cargo.toml` et `lib.rs`

---

## Phase 7 — Codec & Framing (tokio-util)

> Intégration avec tokio pour la communication async.

- [ ] **7.1** Implémenter `src/framing.rs` — length-prefix framing
  - Header : `u32` big-endian = longueur du payload
  - Configurable : taille du header (2 ou 4 octets), endianness
  - `max_packet_size` avec défaut (ex: 16 Mo)
- [ ] **7.2** Implémenter `src/codec.rs``ZrCodec`
  - `impl Encoder<Box<dyn Encode>> for ZrCodec` — écrit header + payload
  - `impl Decoder for ZrCodec` — lit header, vérifie taille, lit payload, retourne `ZrPacket`
  - Gestion propre de `BytesMut` (reserve, split_to, advance)
- [ ] **7.3** Type `FramedPacket` pour le dispatch dynamique
  - Le codec lit l'ID depuis le payload, lookup dans la registry
  - Retourne un `ZrPacket` (type-érasé) que l'utilisateur cast avec un match sur l'ID
- [ ] **7.4** Helper `fn framed_read(stream) -> impl Stream<Item = ZrPacket>` (optionnel, wrapper)
- [ ] **7.5** Tests :
  - [ ] Roundtrip TCP : encode → send → receive → decode → assert_eq
  - [ ] Rejet des packets trop gros
  - [ ] Gestion des reads partiels (TCP peut diviser les données)

---

## Phase 8 — Client & Server Helpers

> Utilitaires pour simplifier l'usage réseau.

- [ ] **8.1** Struct `ZrConnection` wrapper autour de `Framed<TcpStream, ZrCodec>`
  ```rust
  pub struct ZrConnection {
      framed: Framed<TcpStream, ZrCodec>,
  }
  impl ZrConnection {
      pub async fn connect(addr: &str) -> io::Result<Self>;
      pub async fn send_packet<P: Encode + PacketMeta>(&mut self, packet: &P) -> io::Result<()>;
      pub async fn next_packet(&mut self) -> io::Result<Option<ZrPacket>>;
  }
  ```
- [ ] **8.2** Struct `ZrListener` wrapper autour de `TcpListener`
  ```rust
  pub struct ZrListener { listener: TcpListener }
  impl ZrListener {
      pub async fn bind(addr: &str) -> io::Result<Self>;
      pub async fn accept(&self) -> io::Result<(ZrConnection, SocketAddr)>;
  }
  ```
- [ ] **8.3** Macro `#[tokio::main]` compatible — les helpers utilisent tokio derrière
- [ ] **8.4** Tests d'intégration : client/server qui s'échangent des packets

---

## Phase 9 — Exemples

> Démonstrations complètes d'utilisation.

- [ ] **9.1** Exemple `examples/simple.rs` — packet inline, encode/decode sans réseau
- [ ] **9.2** Exemple `examples/login/` — client/server complet
  - Fichiers `.packet` de définition
  - Server qui écoute, reçoit `LoginRequest`, répond `LoginResponse`
  - Client qui se connecte, envoie `LoginRequest`, lit `LoginResponse`
- [ ] **9.3** Exemple avec `Option<T>` et `Vec<T>` pour montrer les types composés
- [ ] **9.4** README avec badges, description, et example usage

---

## Phase 10 — Tests & Qualité

> Fiabilité et robustesse.

- [ ] **10.1** Tests unitaires : chaque type primitif encode/decode roundtrip
- [ ] **10.2** Tests du parser lexer/grammar (bonne et mauvaise syntaxe)
- [ ] **10.3** Tests trybuild : erreurs de compilation avec bons messages
  - [ ] Type inconnu dans `.packet`
  - [ ] Syntaxe invalide
  - [ ] ID manquant
  - [ ] Champ dupliqué
- [ ] **10.4** Tests d'intégration TCP : client ↔ server roundtrip
- [ ] **10.5** Benchmark (criterion) : throughput sérialisation vs bincode
- [ ] **10.6** `cargo clippy --workspace` sans warning
- [ ] **10.7** `cargo fmt --check` passe

---

## Phase 11 — Documentation

- [ ] **11.1** `README.md` avec description, quick start, features
- [ ] **11.2** Doc comments (`///`) sur tous les traits publics
- [ ] **11.3** Doc comments sur le format `.packet` (guide de syntaxe)
- [ ] **11.4** Exemples dans les doc comments (doc-tests)
- [ ] **11.5** CHANGELOG.md

---

## Ordre d'exécution recommandé

```
Phase 0 → Phase 1 → Phase 2 → Phase 3 → Phase 4 → Phase 5

                                          Phase 6 (features)

                                          Phase 7 (codec)

                                      Phase 8 → Phase 9 → Phase 10 → Phase 11
```

**Dépendances critiques :**
- Phase 4 (proc-macro) dépend de Phase 1 (traits) et Phase 2 (AST)
- Phase 7 (codec) dépend de Phase 1 (Encode/Decode) et Phase 4 (PacketMeta)
- Phase 8 (helpers) dépend de Phase 7 (codec)
- Phase 9-11 dépendent de tout le reste

**Peut être fait en parallèle :**
- Phase 5 (builder) peut démarrer après Phase 4
- Phase 6 (features) peut démarrer après Phase 4
- Les tests (Phase 10) peuvent être écrits au fur et à mesure de chaque phase