unistructgen-codegen 0.1.1

Code generation backend for unistructgen - Rust code renderer
Documentation
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
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# πŸ¦€ UniStructGen Codegen

**Π“Π΅Π½Π΅Ρ€Π°Ρ‚ΠΎΡ€ ΠΈΠ΄ΠΈΠΎΠΌΠ°Ρ‚ΠΈΡ‡Π½ΠΎΠ³ΠΎ Rust-ΠΊΠΎΠ΄Π° ΠΈΠ· Intermediate Representation**

[![Crate](https://img.shields.io/crates/v/unistructgen-codegen.svg)](https://crates.io/crates/unistructgen-codegen)
[![Docs](https://docs.rs/unistructgen-codegen/badge.svg)](https://docs.rs/unistructgen-codegen)
[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](../LICENSE-MIT)

---

## πŸ“‹ Π‘ΠΎΠ΄Π΅Ρ€ΠΆΠ°Π½ΠΈΠ΅

- [ΠžΠ±Π·ΠΎΡ€]#-ΠΎΠ±Π·ΠΎΡ€
- [Установка]#-установка
- [Быстрый старт]#-быстрый-старт
- [RustRenderer]#-rustrenderer
- [RenderOptions]#-renderoptions
- [RustRendererBuilder]#-rustrendererbuilder
- [ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Ρ‚ΠΈΠΏΠΎΠ²]#-ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ°-Ρ‚ΠΈΠΏΠΎΠ²
- [Валидация]#-валидация
- [ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибок]#-ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ°-ошибок
- [ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹]#-ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρ‹

---

## 🎯 ΠžΠ±Π·ΠΎΡ€

`unistructgen-codegen` β€” это ΠΌΠΎΠ΄ΡƒΠ»ΡŒ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ Rust-ΠΊΠΎΠ΄Π° ΠΈΠ· IR (Intermediate Representation). Он обСспСчиваСт:

- **Π˜Π΄ΠΈΠΎΠΌΠ°Ρ‚ΠΈΡ‡Π½Ρ‹ΠΉ Rust** β€” ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎΠ΅ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅, ΡΡ‚ΠΈΠ»ΡŒ ΠΈ соглашСния
- **Derive macros** β€” автоматичСскоС Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Debug, Clone, PartialEq, serde
- **ДокумСнтация** β€” сохранСниС doc-ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠ΅Π² ΠΈΠ· IR
- **Атрибуты** β€” ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° #[serde(...)], #[validate(...)], etc.
- **Type safety** β€” ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎΠ΅ ΠΌΠ°ΠΏΠΏΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ IR Ρ‚ΠΈΠΏΠΎΠ² Π² Rust Ρ‚ΠΈΠΏΡ‹
- **Π’Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Π΅ Ρ‚ΠΈΠΏΡ‹** β€” гСнСрация всСх связанных структур

### ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅ΠΌΡ‹Π΅ возмоТности

| Π’ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ | ОписаниС |
|-------------|----------|
| Structs | ΠŸΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹Π΅ структуры с полями |
| Enums | ΠŸΠ΅Ρ€Π΅Ρ‡ΠΈΡΠ»Π΅Π½ΠΈΡ с Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π°ΠΌΠΈ |
| Doc comments | `/// ДокумСнтация` |
| Derives | `#[derive(...)]` |
| Serde attributes | `#[serde(rename = "...")]` |
| Validation | `#[validate(...)]` |
| Nested types | АвтоматичСскоС Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ зависимостСй |
| Option/Vec/Map | `Option<T>`, `Vec<T>`, `HashMap<K, V>` |

---

## πŸ“¦ Установка

```toml
[dependencies]
unistructgen-codegen = "0.1"
unistructgen-core = "0.1"
```

---

## πŸš€ Быстрый старт

```rust
use unistructgen_codegen::{RustRenderer, RenderOptions};
use unistructgen_core::{
    CodeGenerator, IRModule, IRStruct, IRType, IRField,
    IRTypeRef, PrimitiveKind
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Π‘ΠΎΠ·Π΄Π°Ρ‘ΠΌ IR ΠΌΠΎΠ΄ΡƒΠ»ΡŒ
    let mut module = IRModule::new("users".to_string());

    // Π‘ΠΎΠ·Π΄Π°Ρ‘ΠΌ структуру
    let mut user = IRStruct::new("User".to_string());
    user.doc = Some("Represents a user in the system".to_string());
    user.add_derive("Debug".to_string());
    user.add_derive("Clone".to_string());
    user.add_derive("serde::Serialize".to_string());
    user.add_derive("serde::Deserialize".to_string());

    user.add_field(IRField::new(
        "id".to_string(),
        IRTypeRef::Primitive(PrimitiveKind::I64)
    ));

    user.add_field(IRField::new(
        "email".to_string(),
        IRTypeRef::Primitive(PrimitiveKind::String)
    ));

    let mut age = IRField::new(
        "age".to_string(),
        IRTypeRef::Option(Box::new(IRTypeRef::Primitive(PrimitiveKind::I32)))
    );
    age.doc = Some("User's age (optional)".to_string());
    user.add_field(age);

    module.add_type(IRType::Struct(user));

    // Π“Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅ΠΌ ΠΊΠΎΠ΄
    let renderer = RustRenderer::new(RenderOptions::default());
    let code = renderer.generate(&module)?;

    println!("{}", code);

    Ok(())
}
```

**Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚:**

```rust
// Generated by unistructgen v0.1.0
// Do not edit this file manually

#![allow(dead_code)]
#![allow(unused_imports)]

/// Represents a user in the system
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct User {
    pub id: i64,
    pub email: String,
    /// User's age (optional)
    pub age: Option<i32>,
}
```

---

## 🎨 RustRenderer

Основной класс для Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ Rust-ΠΊΠΎΠ΄Π°.

### Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅

```rust
use unistructgen_codegen::{RustRenderer, RenderOptions};

// Π‘ Π΄Π΅Ρ„ΠΎΠ»Ρ‚Π½Ρ‹ΠΌΠΈ опциями
let renderer = RustRenderer::new(RenderOptions::default());

// Π‘ кастомными опциями
let renderer = RustRenderer::new(RenderOptions {
    add_header: false,        // Π‘Π΅Π· Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ° "Generated by..."
    add_clippy_allows: false, // Π‘Π΅Π· #![allow(...)]
});
```

### ΠœΠ΅Ρ‚ΠΎΠ΄Ρ‹

```rust
use unistructgen_core::CodeGenerator;

// Основной ΠΌΠ΅Ρ‚ΠΎΠ΄ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ (Ρ‡Π΅Ρ€Π΅Π· Ρ‚Ρ€Π΅ΠΉΡ‚ CodeGenerator)
let code = renderer.generate(&module)?;

// ΠΠ»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Π½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄ (прямой Π²Ρ‹Π·ΠΎΠ²)
let code = renderer.render(&module)?;

// ΠœΠ΅Ρ‚Π°Π΄Π°Π½Π½Ρ‹Π΅ Π³Π΅Π½Π΅Ρ€Π°Ρ‚ΠΎΡ€Π°
let metadata = renderer.metadata();
println!("Language: {}", renderer.language());    // "Rust"
println!("Extension: {}", renderer.file_extension()); // "rs"

// Валидация IR ΠΏΠ΅Ρ€Π΅Π΄ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠ΅ΠΉ
renderer.validate(&module)?;

// Π€ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ (placeholder для rustfmt)
let formatted = renderer.format(code)?;
```

---

## βš™οΈ RenderOptions

ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ ΠΊΠΎΠ΄Π°.

### ΠžΠΏΡ†ΠΈΠΈ

| ΠžΠΏΡ†ΠΈΡ | Π’ΠΈΠΏ | По ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ | ОписаниС |
|-------|-----|--------------|----------|
| `add_header` | `bool` | `true` | Π”ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ с вСрсиСй |
| `add_clippy_allows` | `bool` | `true` | Π”ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ `#![allow(...)]` |

### ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹

```rust
use unistructgen_codegen::RenderOptions;

// Для production: с Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΎΠΌ ΠΈ clippy allows
let prod_options = RenderOptions::default();

// Для тСстов/макросов: чистый ΠΊΠΎΠ΄
let test_options = RenderOptions {
    add_header: false,
    add_clippy_allows: false,
};

// Волько заголовок, бСз clippy
let custom_options = RenderOptions {
    add_header: true,
    add_clippy_allows: false,
};
```

---

## πŸ—οΈ RustRendererBuilder

Fluent-Π±ΠΈΠ»Π΄Π΅Ρ€ для создания Ρ€Π΅Π½Π΄Π΅Ρ€Π΅Ρ€Π°.

```rust
use unistructgen_codegen::RustRendererBuilder;

let renderer = RustRendererBuilder::new()
    .with_header(true)
    .with_clippy_allows(true)
    .build();
```

---

## πŸ”€ ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Ρ‚ΠΈΠΏΠΎΠ²

### ΠŸΡ€ΠΈΠΌΠΈΡ‚ΠΈΠ²Π½Ρ‹Π΅ Ρ‚ΠΈΠΏΡ‹

| PrimitiveKind | Rust Type |
|---------------|-----------|
| `String` | `String` |
| `I8` | `i8` |
| `I16` | `i16` |
| `I32` | `i32` |
| `I64` | `i64` |
| `I128` | `i128` |
| `U8` | `u8` |
| `U16` | `u16` |
| `U32` | `u32` |
| `U64` | `u64` |
| `U128` | `u128` |
| `F32` | `f32` |
| `F64` | `f64` |
| `Bool` | `bool` |
| `Char` | `char` |
| `DateTime` | `chrono::DateTime<chrono::Utc>` |
| `Uuid` | `uuid::Uuid` |
| `Decimal` | `rust_decimal::Decimal` |
| `Json` | `serde_json::Value` |

### БоставныС Ρ‚ΠΈΠΏΡ‹

```rust
// Option<T>
IRTypeRef::Option(Box::new(IRTypeRef::Primitive(PrimitiveKind::String)))
// β†’ Option<String>

// Vec<T>
IRTypeRef::Vec(Box::new(IRTypeRef::Primitive(PrimitiveKind::I32)))
// β†’ Vec<i32>

// HashMap<K, V>
IRTypeRef::Map(
    Box::new(IRTypeRef::Primitive(PrimitiveKind::String)),
    Box::new(IRTypeRef::Primitive(PrimitiveKind::I64))
)
// β†’ std::collections::HashMap<String, i64>

// Бсылка Π½Π° Ρ‚ΠΈΠΏ
IRTypeRef::Named("Address".to_string())
// β†’ Address
```

### Π’Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Π΅ Ρ‚ΠΈΠΏΡ‹

```rust
// Option<Vec<String>>
IRTypeRef::Option(Box::new(
    IRTypeRef::Vec(Box::new(
        IRTypeRef::Primitive(PrimitiveKind::String)
    ))
))
// β†’ Option<Vec<String>>

// Vec<HashMap<String, User>>
IRTypeRef::Vec(Box::new(
    IRTypeRef::Map(
        Box::new(IRTypeRef::Primitive(PrimitiveKind::String)),
        Box::new(IRTypeRef::Named("User".to_string()))
    )
))
// β†’ Vec<std::collections::HashMap<String, User>>
```

---

## βœ… Валидация

Π“Π΅Π½Π΅Ρ€Π°Ρ‚ΠΎΡ€ Π²Π°Π»ΠΈΠ΄ΠΈΡ€ΡƒΠ΅Ρ‚ Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚Ρ‹ ΠΈΠ· `FieldConstraints` ΠΈ создаёт `#[validate(...)]`.

### ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅ΠΌΡ‹Π΅ ограничСния

```rust
use unistructgen_core::FieldConstraints;

let constraints = FieldConstraints {
    // Π”Π»ΠΈΠ½Π° строки/массива
    min_length: Some(3),
    max_length: Some(100),
    // β†’ #[validate(length(min = 3, max = 100))]

    // Π”ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½ чисСл
    min_value: Some(0.0),
    max_value: Some(100.0),
    // β†’ #[validate(range(min = 0, max = 100))]

    // Regex ΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½
    pattern: Some(r"^\w+$".to_string()),
    // β†’ #[validate(regex = "^\w+$")]

    // Π€ΠΎΡ€ΠΌΠ°Ρ‚
    format: Some("email".to_string()),
    // β†’ #[validate(email)]
};
```

### ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ

```rust
let mut field = IRField::new("email".to_string(), IRTypeRef::Primitive(PrimitiveKind::String));
field.constraints = FieldConstraints {
    min_length: Some(5),
    max_length: Some(255),
    format: Some("email".to_string()),
    ..Default::default()
};

// Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚:
// #[validate(length(min = 5, max = 255), email)]
// pub email: String,
```

---

## ❌ ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибок

### CodegenError

```rust
use unistructgen_codegen::CodegenError;

pub enum CodegenError {
    /// Ошибка Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°
    RenderError {
        component: String,  // "struct", "field", "enum"
        context: String,    // Имя Ρ‚ΠΈΠΏΠ°
        message: String,
    },

    /// Ошибка форматирования
    FormatError {
        context: String,
        source: std::fmt::Error,
    },

    /// Ошибка Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ
    ValidationError {
        reason: String,
        suggestion: Option<String>,
    },

    /// НСвалидный ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€
    InvalidIdentifier {
        name: String,
        context: String,  // "struct name", "field name"
        reason: String,
    },

    /// НСподдСрТиваСмый Ρ‚ΠΈΠΏ
    UnsupportedType {
        type_name: String,
        context: String,
        reason: String,
        alternative: Option<String>,
    },

    /// ΠŸΡ€Π΅Π²Ρ‹ΡˆΠ΅Π½Π° максимальная Π³Π»ΡƒΠ±ΠΈΠ½Π° влоТСнности
    MaxDepthExceeded {
        context: String,
        max_depth: usize,
    },
}
```

### ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибок

```rust
use unistructgen_codegen::{RustRenderer, RenderOptions, CodegenError};

let renderer = RustRenderer::new(RenderOptions::default());

match renderer.generate(&module) {
    Ok(code) => println!("{}", code),
    Err(CodegenError::ValidationError { reason, suggestion }) => {
        eprintln!("Validation failed: {}", reason);
        if let Some(sug) = suggestion {
            eprintln!("Suggestion: {}", sug);
        }
    },
    Err(CodegenError::InvalidIdentifier { name, context, reason }) => {
        eprintln!("Invalid {} '{}': {}", context, name, reason);
    },
    Err(e) => eprintln!("Error: {}", e),
}
```

### Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ suggestions

```rust
use unistructgen_codegen::CodegenError;

let error = CodegenError::validation_error("Module is empty")
    .with_suggestion("Add at least one struct or enum to the module");
```

---

## πŸ“ ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹

### ГСнСрация структуры с serde

```rust
use unistructgen_codegen::{RustRenderer, RenderOptions};
use unistructgen_core::*;

let mut user = IRStruct::new("User".to_string());
user.add_derive("serde::Serialize".to_string());
user.add_derive("serde::Deserialize".to_string());

let mut id_field = IRField::new("id".to_string(), IRTypeRef::Primitive(PrimitiveKind::I64));

let mut name_field = IRField::new("user_name".to_string(), IRTypeRef::Primitive(PrimitiveKind::String));
name_field.source_name = Some("userName".to_string());
name_field.attributes.push("serde(rename = \"userName\")".to_string());

user.add_field(id_field);
user.add_field(name_field);

let mut module = IRModule::new("users".to_string());
module.add_type(IRType::Struct(user));

let renderer = RustRenderer::new(RenderOptions::default());
let code = renderer.generate(&module)?;
```

**Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚:**

```rust
#[derive(serde::Serialize, serde::Deserialize)]
pub struct User {
    pub id: i64,
    #[serde(rename = "userName")]
    pub user_name: String,
}
```

### ГСнСрация enum

```rust
use unistructgen_core::*;

let status = IREnum {
    name: "OrderStatus".to_string(),
    variants: vec![
        IREnumVariant {
            name: "Pending".to_string(),
            source_value: None,
            doc: Some("Order is pending".to_string()),
        },
        IREnumVariant {
            name: "InProgress".to_string(),
            source_value: Some("in_progress".to_string()),
            doc: None,
        },
        IREnumVariant {
            name: "Completed".to_string(),
            source_value: None,
            doc: None,
        },
    ],
    derives: vec!["Debug".to_string(), "Clone".to_string(), "serde::Serialize".to_string()],
    doc: Some("Order status enum".to_string()),
};

let mut module = IRModule::new("orders".to_string());
module.add_type(IRType::Enum(status));

let renderer = RustRenderer::new(RenderOptions::default());
let code = renderer.generate(&module)?;
```

**Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚:**

```rust
/// Order status enum
#[derive(Debug, Clone, serde::Serialize)]
pub enum OrderStatus {
    /// Order is pending
    Pending,
    #[serde(rename = "in_progress")]
    InProgress,
    Completed,
}
```

### ГСнСрация с Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠ΅ΠΉ

```rust
let mut email_field = IRField::new(
    "email".to_string(),
    IRTypeRef::Primitive(PrimitiveKind::String)
);
email_field.doc = Some("User's email address".to_string());
email_field.constraints = FieldConstraints {
    min_length: Some(5),
    max_length: Some(255),
    format: Some("email".to_string()),
    pattern: Some(r"^[\w@.]+$".to_string()),
    ..Default::default()
};

let mut age_field = IRField::new(
    "age".to_string(),
    IRTypeRef::Primitive(PrimitiveKind::I32)
);
age_field.constraints = FieldConstraints {
    min_value: Some(0.0),
    max_value: Some(150.0),
    ..Default::default()
};
```

**Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚:**

```rust
/// User's email address
#[validate(length(min = 5, max = 255), regex = "^[\w@.]+$", email)]
pub email: String,

#[validate(range(min = 0, max = 150))]
pub age: i32,
```

---

## πŸ”— БвязанныС ΠΌΠΎΠ΄ΡƒΠ»ΠΈ

- [unistructgen-core]../core/README.md β€” IR, Ρ‚Ρ€Π΅ΠΉΡ‚Ρ‹, pipeline
- [unistructgen-json-parser]../parsers/json_parser/README.md β€” JSON β†’ IR
- [unistructgen-markdown-parser]../parsers/markdown_parser/README.md β€” Markdown β†’ IR
- [unistructgen-openapi-parser]../parsers/openapi_parser/README.md β€” OpenAPI β†’ IR

---

## πŸ—Ί Roadmap

- [ ] Π˜Π½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΡ с `rustfmt` для форматирования
- [ ] ГСнСрация `impl` Π±Π»ΠΎΠΊΠΎΠ²
- [ ] ГСнСрация `From`/`Into` Ρ‚Ρ€Π΅ΠΉΡ‚ΠΎΠ²
- [ ] ГСнСрация `Builder` ΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½Π°
- [ ] ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° `#[cfg(...)]` Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚ΠΎΠ²

---

## πŸ“œ ЛицСнзия

MIT ΠΈΠ»ΠΈ Apache-2.0 β€” Π½Π° ваш Π²Ρ‹Π±ΠΎΡ€.