nulid_derive 0.10.1

Derive macros for nulid wrapper types
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
# nulid_derive

Derive macros for types that wrap `Nulid`.

This crate provides procedural macros to automatically implement common traits for newtype wrappers around `Nulid`, eliminating boilerplate code.

## Features

### Core Traits

The `Id` derive macro automatically implements:

- `TryFrom<String>` - Parse from owned String
- `TryFrom<&str>` - Parse from string slice
- `From<Nulid>` - Create wrapper from Nulid
- `From<WrapperType> for Nulid` - Extract inner Nulid
- `AsRef<Nulid>` - Borrow inner Nulid
- `Deref<Target = Nulid>` - Direct access to all Nulid methods
- `DerefMut` - Mutable access to inner Nulid
- `core::fmt::Display` - Format as Base32 string
- `core::fmt::Debug` - Debug formatting
- `core::str::FromStr` - Parse from string using `.parse()`
- `Copy` - Value semantics (automatically provides `Clone`)
- `PartialEq` and `Eq` - Equality comparison with other wrappers
- `PartialEq<Nulid>` - Direct equality comparison with `Nulid`
- `PartialOrd` and `Ord` - Ordering comparison with other wrappers
- `PartialOrd<Nulid>` - Direct ordering comparison with `Nulid`
- `Hash` - Hashing support for collections
- `Default` - Creates a default instance with `Nulid::ZERO`

### Constructor Methods

It also provides:

- `new()` method - Creates a new instance with a freshly generated `Nulid`
- `now()` method - Alias for `new()`
- `nil()` method - Creates a nil/zero instance
- `min()` method - Returns the minimum possible instance
- `max()` method - Returns the maximum possible instance
- `from_datetime(SystemTime)` - Creates from specific time
- `from_nanos(u128, u64)` - Creates from timestamp and random
- `from_u128(u128)` - Creates from raw u128
- `from_bytes([u8; 16])` - Creates from byte array

### Feature-Gated Traits

When the corresponding features are enabled, additional trait implementations are automatically generated:

#### `serde` feature

- `Serialize` - Serialization support for JSON, bincode, etc.
- `Deserialize` - Deserialization support

```toml
[dependencies]
# The 'serde' feature is automatically propagated to nulid_derive
nulid = { version = "0.5", features = ["derive", "serde"] }
```

```rust
use nulid::Id;
use serde::{Serialize, Deserialize};

#[derive(Id)]  // Automatically implements Serialize + Deserialize
pub struct UserId(nulid::Nulid);

fn main() -> nulid::Result<()> {
    let user_id = UserId::new()?;

    // Serialize to JSON
    let json = serde_json::to_string(&user_id)?;

    // Deserialize from JSON
    let parsed: UserId = serde_json::from_str(&json)?;

    Ok(())
}
```

#### `chrono` feature

- `chrono_datetime()` method - Convert to `chrono::DateTime<Utc>`
- `from_chrono_datetime(DateTime<Utc>)` method - Create from chrono DateTime

```toml
[dependencies]
# The 'chrono' feature is automatically propagated to nulid_derive
nulid = { version = "0.5", features = ["derive", "chrono"] }
```

```rust
use nulid::Id;
use chrono::{DateTime, Utc};

#[derive(Id)]  // Automatically implements chrono methods
pub struct UserId(nulid::Nulid);

fn main() -> nulid::Result<()> {
    let user_id = UserId::new()?;

    // Convert to chrono DateTime
    let dt: DateTime<Utc> = user_id.chrono_datetime();
    println!("User ID timestamp: {}", dt);

    // Create from chrono DateTime
    let now = Utc::now();
    let user_id2 = UserId::from_chrono_datetime(now)?;

    Ok(())
}
```

#### `uuid` feature

- `From<uuid::Uuid>` - Convert from UUID
- `Into<uuid::Uuid>` - Convert to UUID
- `to_uuid()` method - Convert to UUID
- `from_uuid(Uuid)` method - Create from UUID

```toml
[dependencies]
# The 'uuid' feature is automatically propagated to nulid_derive
nulid = { version = "0.5", features = ["derive", "uuid"] }
```

```rust
use nulid::Id;
use uuid::Uuid;

#[derive(Id)]  // Automatically implements UUID conversions
pub struct UserId(nulid::Nulid);

fn main() -> nulid::Result<()> {
    let user_id = UserId::new()?;

    // Convert to UUID
    let uuid = user_id.to_uuid();

    // Convert from UUID
    let from_uuid = UserId::from_uuid(uuid);

    // Using From/Into traits
    let uuid2: Uuid = user_id.into();
    let user_id2: UserId = uuid.into();

    Ok(())
}
```

#### `sqlx` feature

- `Type<Postgres>` - PostgreSQL type support
- `Encode<Postgres>` - Encoding for PostgreSQL
- `Decode<Postgres>` - Decoding from PostgreSQL
- `PgHasArrayType` - Array type support

```toml
[dependencies]
# The 'sqlx' feature is automatically propagated to nulid_derive
nulid = { version = "0.5", features = ["derive", "sqlx"] }
sqlx = { version = "0.8", features = ["postgres", "uuid"] }
```

```rust
use nulid::Id;
use sqlx::PgPool;

#[derive(Id)]  // Automatically implements SQLx traits
pub struct UserId(nulid::Nulid);

#[derive(sqlx::FromRow)]
struct User {
    id: UserId,  // Can be used directly in SQLx queries!
    name: String,
}

async fn insert_user(pool: &PgPool, id: UserId, name: &str) -> sqlx::Result<()> {
    sqlx::query("INSERT INTO users (id, name) VALUES ($1, $2)")
        .bind(id)  // UserId can be bound directly
        .bind(name)
        .execute(pool)
        .await?;
    Ok(())
}
```

#### `postgres-types` feature

- `FromSql` - Deserialize from PostgreSQL
- `ToSql` - Serialize to PostgreSQL

```toml
[dependencies]
# The 'postgres-types' feature is automatically propagated to nulid_derive
nulid = { version = "0.5", features = ["derive", "postgres-types"] }
postgres-types = "0.2"
```

```rust
use nulid::Id;
use postgres_types::{ToSql, FromSql};

#[derive(Id)]  // Automatically implements ToSql + FromSql
pub struct UserId(nulid::Nulid);

// Can now be used with the postgres crate
// let row = client.query_one("SELECT id FROM users WHERE id = $1", &[&user_id])?;
```

#### `proto` feature

- `to_proto()` method - Convert to protobuf message
- `from_proto(ProtoNulid)` method - Create from protobuf message
- `From<WrapperType> for ProtoNulid` - Convert to protobuf
- `From<ProtoNulid> for WrapperType` - Convert from protobuf

```toml
[dependencies]
# The 'proto' feature is automatically propagated to nulid_derive
nulid = { version = "0.5", features = ["derive", "proto"] }
prost = "0.14"
```

```rust
use nulid::Id;
use nulid::proto::nulid::Nulid as ProtoNulid;
use prost::Message;

#[derive(Id)]  // Automatically implements protobuf conversions
pub struct UserId(nulid::Nulid);

fn main() -> nulid::Result<()> {
    let user_id = UserId::new()?;

    // Convert to protobuf message
    let proto = user_id.to_proto();

    // Encode to bytes
    let encoded = proto.encode_to_vec();

    // Decode from bytes
    let decoded = ProtoNulid::decode(&*encoded).unwrap();

    // Convert back to UserId
    let user_id2 = UserId::from_proto(decoded);

    // Using From/Into traits
    let proto2: ProtoNulid = user_id.into();
    let user_id3: UserId = proto2.into();

    Ok(())
}
```

### Feature Propagation

**Important**: When you enable the `derive` feature along with other features (like `serde`, `uuid`, `sqlx`, `postgres-types`, `proto`, or `chrono`) on the `nulid` crate, those features are **automatically propagated** to `nulid_derive`. You don't need to enable them separately on both crates.

```toml
#  Correct - features are automatically propagated to nulid_derive
[dependencies]
nulid = { version = "0.5", features = ["derive", "serde", "uuid", "sqlx"] }

#  Not necessary - you don't need to enable features on nulid_derive manually
[dependencies]
nulid = { version = "0.5", features = ["derive", "serde"] }
nulid_derive = { version = "0.5", features = ["serde"] }  # This is redundant
```

This automatic propagation works for all feature-gated traits:

- `serde` → enables `Serialize` and `Deserialize` implementations
- `chrono` → enables chrono `DateTime` conversion methods
- `uuid` → enables UUID conversion traits
- `sqlx` → enables SQLx PostgreSQL traits
- `postgres-types` → enables `FromSql` and `ToSql` traits
- `proto` → enables Protocol Buffers conversion methods

## Basic Usage

Add this to your `Cargo.toml`:

```toml
[dependencies]
nulid = { version = "0.5", features = ["derive"] }
```

Then use the derive macro on your wrapper types:

```rust
use nulid::{Nulid, Id};

#[derive(Id)]
pub struct UserId(Nulid);

#[derive(Id)]
pub struct OrderId(pub Nulid);

fn main() -> nulid::Result<()> {
    // Create new ID with fresh NULID
    let user_id = UserId::new()?;

    // Create default ID (ZERO)
    let default_id = UserId::default();

    // Parse from &str
    let user_id2 = UserId::try_from("01H0JQ4VEFSBV974PRXXWEK5ZW")?;

    // Parse from String
    let user_id3 = UserId::try_from("01H0JQ4VEFSBV974PRXXWEK5ZW".to_string())?;

    // Parse using FromStr
    let user_id4: UserId = "01H0JQ4VEFSBV974PRXXWEK5ZW".parse()?;

    // Create from Nulid
    let nulid = Nulid::new()?;
    let order_id = OrderId::from(nulid);

    // Extract inner Nulid
    let back_to_nulid: Nulid = order_id.into();

    // Borrow inner Nulid
    let nulid_ref: &Nulid = order_id.as_ref();

    // Display as string
    println!("User ID: {}", user_id);

    // Direct comparison with Nulid
    assert_eq!(order_id, nulid);
    assert!(order_id <= nulid);

    // Access Nulid methods directly via Deref
    let nanos = user_id.nanos();
    let random = user_id.random();
    let (timestamp, rand) = user_id.parts();
    println!("Timestamp: {}, Random: {}", timestamp, rand);

    Ok(())
}
```

## Direct Access to Nulid Methods

With `Deref` and `DerefMut` traits, wrapper types can directly access all `Nulid` methods without needing to extract or dereference the inner value:

```rust
use nulid::Id;

#[derive(Id)]
pub struct UserId(nulid::Nulid);

fn main() -> nulid::Result<()> {
    let user_id = UserId::new()?;

    // Access timestamp methods directly
    let nanos = user_id.nanos();           // Get nanoseconds
    let micros = user_id.micros();         // Get microseconds
    let millis = user_id.millis();         // Get milliseconds
    let seconds = user_id.seconds();       // Get seconds
    let subsec = user_id.subsec_nanos();   // Get subsecond nanoseconds

    // Access random component
    let random = user_id.random();

    // Get both parts
    let (timestamp, rand) = user_id.parts();

    // Convert to different formats
    let as_u128 = user_id.as_u128();
    let as_bytes = user_id.to_bytes();

    // Check if nil
    let default_id = UserId::default();
    assert!(default_id.is_nil());
    assert!(!user_id.is_nil());

    // All Nulid methods are available directly on UserId!
    Ok(())
}
```

## Requirements

The derive macro requires:

1. The type must be a tuple struct
2. It must have exactly one field
3. That field must be of type `Nulid`

Valid examples:

```rust
#[derive(Id)]
pub struct UserId(Nulid);           //  Private field

#[derive(Id)]
pub struct OrderId(pub Nulid);      //  Public field
```

Invalid examples:

```rust
#[derive(Id)]
pub struct UserId {                 //  Not a tuple struct
    nulid: Nulid,
}

#[derive(Id)]
pub struct UserId(Nulid, String);   //  Multiple fields

#[derive(Id)]
pub struct UserId(String);          //  Wrong type
```

## Type Safety

Using wrapper types provides type safety by preventing accidental mixing of different ID types:

```rust
use nulid::{Nulid, Id};

#[derive(Id)]
pub struct UserId(Nulid);

#[derive(Id)]
pub struct OrderId(Nulid);

fn process_user(id: UserId) { /* ... */ }
fn process_order(id: OrderId) { /* ... */ }

let user_id = UserId::from(Nulid::new()?);
let order_id = OrderId::from(Nulid::new()?);

process_user(user_id);   //  Correct type
// process_user(order_id);  //  Compile error: expected UserId, found OrderId
```

## Error Handling

All parsing methods return `Result<T, nulid::Error>`, allowing proper error handling:

```rust
use nulid::{Error, Id};

#[derive(Id)]
pub struct UserId(nulid::Nulid);

match UserId::try_from("invalid-string") {
    Ok(id) => println!("Parsed: {}", id),
    Err(Error::InvalidLength { expected, found }) => {
        eprintln!("Wrong length: expected {}, got {}", expected, found);
    }
    Err(Error::InvalidChar(ch, pos)) => {
        eprintln!("Invalid character '{}' at position {}", ch, pos);
    }
    Err(e) => eprintln!("Parse error: {}", e),
}
```

## Integration with Other Traits

The derive macro works well with other derive macros and automatically provides feature-gated trait implementations:

```rust
use nulid::{Nulid, Id};

#[derive(Id)]
pub struct UserId(Nulid);

// Standard traits are automatically implemented:
// Debug, Copy (Clone), PartialEq, Eq, Hash, PartialOrd, Ord

// With features enabled, additional traits are automatically implemented:
// - serde feature: Serialize, Deserialize
// - chrono feature: chrono_datetime(), from_chrono_datetime()
// - uuid feature: From<Uuid>, Into<Uuid>
// - sqlx feature: Type<Postgres>, Encode, Decode
// - postgres-types feature: FromSql, ToSql
// - proto feature: to_proto(), from_proto(), From<ProtoNulid>
```

## Combining Multiple Features

You can enable multiple features at once to get all the trait implementations you need:

```toml
[dependencies]
nulid = { version = "0.5", features = ["derive", "serde", "uuid", "sqlx", "chrono", "proto"] }
```

```rust
use nulid::Id;

#[derive(Id)]
pub struct UserId(nulid::Nulid);

// Now UserId automatically implements:
// - All core traits (Debug, Copy, PartialEq, etc.)
// - Serde traits (Serialize, Deserialize)
// - Chrono methods (chrono_datetime(), from_chrono_datetime())
// - UUID conversions (From<Uuid>, Into<Uuid>)
// - SQLx traits (Type<Postgres>, Encode, Decode)
// - Proto methods (to_proto(), from_proto())
// Plus all the constructor methods (new, nil, min, max, etc.)
```

## Examples

See the [examples directory](https://github.com/kakilangit/nulid/tree/main/examples) in the nulid repository for more usage examples.

## License

This project is licensed under the MIT License - see the LICENSE file for details.