nulid 0.10.1

Nanosecond-Precision Universally Lexicographically Sortable Identifier
Documentation
//! Example demonstrating NULID serialization with serde.
//!
//! Includes JSON, `MessagePack`, TOML, and Bincode examples.
//!
//! Run with: `cargo run --example serde_example --features serde`

#![allow(clippy::unwrap_used)]
#![allow(clippy::too_many_lines)]
#![allow(clippy::cast_precision_loss)]

use nulid::Nulid;
use serde::{Deserialize, Serialize};

fn main() -> Result<(), Box<dyn core::error::Error>> {
    // Define structs at the top of function
    #[derive(Debug, Serialize, Deserialize, PartialEq)]
    struct User {
        id: Nulid,
        name: String,
        email: String,
        created_at: Nulid,
    }

    #[derive(Debug, Serialize, Deserialize)]
    struct Database {
        users: Vec<User>,
        total_count: usize,
    }

    println!("NULID Serde Integration Example");
    println!("================================\n");

    // Create a user with NULID identifiers
    println!("1. Creating a User struct with NULID fields...");
    let user = User {
        id: Nulid::new()?,
        name: "Alice Johnson".to_string(),
        email: "alice@example.com".to_string(),
        created_at: Nulid::new()?,
    };
    let user_id = user.id;
    let created_at = user.created_at;
    println!("   User ID: {user_id}");
    println!("   Created At: {created_at}");
    println!();

    // Serialize to JSON
    println!("2. Serializing to JSON...");
    let json = serde_json::to_string_pretty(&user)?;
    println!("   JSON:\n{json}");
    println!();

    // Deserialize from JSON
    println!("3. Deserializing from JSON...");
    let deserialized: User = serde_json::from_str(&json)?;
    let deser_id = deserialized.id;
    println!("   Deserialized User ID: {deser_id}");
    let match_result = if user == deserialized { "ok" } else { "FAIL" };
    println!("   Match: {match_result}");
    println!();

    println!("4. Working with collections...");
    let mut users = Vec::new();
    for i in 0..3 {
        users.push(User {
            id: Nulid::new()?,
            name: format!("User {}", i + 1),
            email: format!("user{}@example.com", i + 1),
            created_at: Nulid::new()?,
        });
    }

    let db = Database {
        total_count: users.len(),
        users,
    };

    let db_json = serde_json::to_string_pretty(&db)?;
    println!("   Database JSON:\n{db_json}");
    println!();

    // Deserialize the database
    println!("5. Deserializing database...");
    let deserialized_db: Database = serde_json::from_str(&db_json)?;
    let total_count = deserialized_db.total_count;
    println!("   Total users: {total_count}");
    for (i, user_item) in deserialized_db.users.iter().enumerate() {
        let idx = i + 1;
        let name = &user_item.name;
        let id = user_item.id;
        println!("   [{idx}] {name} - {id}");
    }
    println!();

    // Demonstrate MessagePack serialization
    println!("6. MessagePack serialization...");
    let msgpack_bytes = rmp_serde::to_vec(&user)?;
    let bytes_len = msgpack_bytes.len();
    println!("   MessagePack size: {bytes_len} bytes");
    let msgpack_user: User = rmp_serde::from_slice(&msgpack_bytes)?;
    let msgpack_id = msgpack_user.id;
    println!("   Deserialized ID: {msgpack_id}");
    let match_result = if user.id == msgpack_user.id {
        "ok"
    } else {
        "FAIL"
    };
    println!("   Match: {match_result}");
    println!();

    // Demonstrate TOML serialization
    println!("7. TOML serialization...");
    let toml_str = toml::to_string_pretty(&user)?;
    println!("   TOML:\n{toml_str}");

    // TOML deserialization may have issues with string lifetimes in some versions
    match toml::from_str::<User>(&toml_str) {
        Ok(toml_user) => {
            let toml_match = if user == toml_user { "ok" } else { "FAIL" };
            println!("   Deserialized successfully");
            println!("   Match: {toml_match}");
        }
        Err(e) => {
            println!("   Note: TOML deserialization has known issues with some types");
            println!("   Error details: {e}");
            println!("   Serialization to TOML works, deserialization skipped");
        }
    }
    println!();

    // Demonstrate sorting with serialized data
    println!("8. Sorting NULIDs maintains order after serialization...");
    let mut nulids = [
        Nulid::new()?,
        Nulid::new()?,
        Nulid::new()?,
        Nulid::new()?,
        Nulid::new()?,
    ];

    // Sort NULIDs
    nulids.sort();
    println!("   Sorted NULIDs:");
    for (i, id) in nulids.iter().enumerate() {
        let idx = i + 1;
        println!("     [{idx}] {id}");
    }
    println!();

    // Serialize the sorted NULIDs to JSON
    let json_ids: Vec<String> = nulids
        .iter()
        .filter_map(|id| serde_json::to_string(id).ok())
        .collect();

    // Verify JSON strings maintain sort order
    let mut json_ids_sorted = json_ids.clone();
    json_ids_sorted.sort();
    let order_maintained = if json_ids == json_ids_sorted {
        "ok"
    } else {
        "FAIL"
    };
    println!("   JSON serialized strings maintain sort order: {order_maintained}");
    println!();

    // Demonstrate Bincode serialization
    println!("9. Bincode binary serialization...");
    let bincode_encoded = bincode::serde::encode_to_vec(user.id, bincode::config::standard())?;
    let bincode_size = bincode_encoded.len();
    println!("   Bincode size: {bincode_size} bytes");
    let (bincode_decoded, _): (Nulid, usize) =
        bincode::serde::decode_from_slice(&bincode_encoded, bincode::config::standard())?;
    let bincode_match = if user.id == bincode_decoded {
        "ok"
    } else {
        "FAIL"
    };
    println!("   Deserialized ID: {bincode_decoded}");
    println!("   Match: {bincode_match}");
    println!();

    // Size comparison
    println!("10. Size comparison across formats...");
    let test_nulid = Nulid::new()?;
    let json_bytes = serde_json::to_string(&test_nulid)?.len();
    let msgpack_bytes = rmp_serde::to_vec(&test_nulid)?.len();
    let bincode_bytes =
        bincode::serde::encode_to_vec(test_nulid, bincode::config::standard())?.len();

    println!("   JSON:        {json_bytes} bytes");
    println!("   MessagePack: {msgpack_bytes} bytes");
    println!("   Bincode:     {bincode_bytes} bytes (most compact)");
    println!();

    // Bincode with collections
    println!("11. Bincode with multiple NULIDs...");
    let nulid_vec = vec![Nulid::new()?, Nulid::new()?, Nulid::new()?];
    let vec_encoded = bincode::serde::encode_to_vec(&nulid_vec, bincode::config::standard())?;
    let vec_size = vec_encoded.len();
    let avg_size = vec_size as f64 / nulid_vec.len() as f64;
    println!("   Serialized 3 NULIDs: {vec_size} bytes");
    println!("   Average per NULID: {avg_size:.1} bytes");

    let (vec_decoded, _): (Vec<Nulid>, usize) =
        bincode::serde::decode_from_slice(&vec_encoded, bincode::config::standard())?;
    let vec_match = if nulid_vec == vec_decoded {
        "ok"
    } else {
        "FAIL"
    };
    println!("   Round-trip match: {vec_match}");
    println!();

    println!("All serde examples completed successfully!");

    Ok(())
}

#[cfg(not(feature = "serde"))]
fn main() {
    eprintln!("This example requires the 'serde' feature.");
    eprintln!("Run with: cargo run --example serde_example --features serde");
    core::process::exit(1);
}