# puuid

[![Latest Version](https://img.shields.io/crates/v/puuid.svg)](https://crates.io/crates/puuid)

**Type-safe, readable IDs. Just like Stripe.**

Raw UUIDs are annoying. When you see `550e8400-e29b...` in your logs, you have no idea if that's a User ID, an Order ID, or an API Key.

Even worse, if you have a function `fn process(user_id: Uuid, order_id: Uuid)`, it is terrifyingly easy to swap the arguments by mistake. The compiler won't catch it.

**puuid** fixes this by adding **Prefixes** and **Type Safety**.

## The Result

```rust
// ❌ Before: Mystery Strings
"018c6427-4f30-7f89-a1b2-c3d4e5f67890"

// ✅ After: Self-Describing IDs
"user_018c6427-4f30-7f89-a1b2-c3d4e5f67890"
"ord_018c6427-4f30-7f89-a1b2-c3d4e5f67890"
```

## Installation

Add this to your `Cargo.toml`:

```toml
[dependencies]
puuid = { version = "0.1", features = ["serde", "v7"] }
```

*Features available: `v4` (random), `v7` (time-sorted, recommended), `serde`, `v1`, `v3`, `v5`.*

## How to use it

### 1. The Setup

You define your prefixes once, usually in a `models.rs` or `types.rs` file.

```rust
use puuid::{Puuid, prefix};

// Define the prefixes
prefix!(User, "user");
prefix!(Order, "ord");
prefix!(ApiKey, "sk");

// Define your strong types
pub type UserId = Puuid<User>;
pub type OrderId = Puuid<Order>;
pub type SecretKey = Puuid<ApiKey>;
```

### 2. Generating IDs

By default, we recommend **UUID v7**. They are sortable by time (great for databases) and random enough to be unique.

```rust
fn main() {
    let user_id = UserId::new_v7();
    let order_id = OrderId::new_v7();

    println!("Created User: {}", user_id); 
    // Output: user_018c6427-4f30-7f89-a1b2-c3d4e5f67890
}
```

### 3. Type Safety (The Best Part)

The compiler now protects you from mixing up IDs.

```rust
fn delete_order(id: OrderId) {
    println!("Deleting order: {}", id);
}

fn main() {
    let user_id = UserId::new_v7();

    // ❌ Compile Error: expected OrderId, found UserId
    // delete_order(user_id); 
}
```

### 4. Serde Integration

If you enable the `serde` feature, `puuid` handles JSON serialization and deserialization automatically. It even validates the prefix for you!

```rust
#[derive(Serialize, Deserialize)]
struct CheckoutSession {
    id: OrderId,
    customer: UserId,
}

fn main() {
    // If the JSON string starts with "ord_", it works.
    // If it starts with "user_" (or is just a raw UUID), it fails deserialization.
    let json = r#"{ 
        "id": "ord_018...", 
        "customer": "user_018..." 
    }"#;
    
    let session: CheckoutSession = serde_json::from_str(json).unwrap();
}
```

## Common Questions

**Does this add overhead?**
Zero. `Puuid<T>` is a "New Type" wrapper around the standard `uuid::Uuid`. It compiles down to the exact same thing as a raw UUID.

**Can I use standard UUID methods?**
Yes. `Puuid` implements `Deref`, so you can call any method from the `uuid` crate directly on it.
```rust
let id = UserId::new_v7();
let bytes = id.as_bytes(); // Works directly
let raw = id.into_inner(); // Extracts the raw uuid::Uuid
```

**How do I store this in a Database?**
*   **Postgres/MySQL:** Store it as a `TEXT` or `VARCHAR` column to keep the prefix visible.
*   **Performance critical:** You can store it as `UUID` (binary) by calling `.into_inner()` before inserting, but you lose the prefix in the DB.

## License

MIT. Use it freely.
