at-jet 0.7.2

High-performance HTTP + Protobuf API framework for mobile services
Documentation
//! Basic AT-Jet Client Example
//!
//! Demonstrates how to use the JetClient for both Protobuf and JSON formats.
//!
//! Run with: cargo run --example basic_client
//!
//! Make sure the server is running first: cargo run --example basic_server
//!
//! Features demonstrated:
//! - JetClient for type-safe Protobuf requests (production)
//! - JetClient for JSON debug requests (debugging)

use {at_jet::prelude::*,
     serde::{Deserialize,
             Serialize},
     tracing_subscriber::EnvFilter};

// Include generated protobuf code
pub mod proto {
  include!(concat!(env!("OUT_DIR"), "/at_jet.example.rs"));
}

use proto::{CreateUserRequest,
            DeleteUserResponse,
            ListUsersResponse,
            User};

// JSON-compatible types for debug requests
// (In real apps, you'd derive these on your proto types or use a shared definition)
#[derive(Debug, Serialize, Deserialize)]
struct UserJson {
  id:         i32,
  name:       String,
  email:      String,
  created_at: i64,
}

#[derive(Debug, Serialize, Deserialize)]
struct ListUsersResponseJson {
  users:     Vec<UserJson>,
  total:     i32,
  page:      i32,
  page_size: i32,
}

#[derive(Debug, Serialize, Deserialize)]
struct CreateUserRequestJson {
  name:  String,
  email: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct DeleteUserResponseJson {
  success: bool,
  message: String,
}

const DEBUG_KEY: &str = "dev-debug-key";

#[tokio::main]
async fn main() -> anyhow::Result<()> {
  // Initialize logging
  tracing_subscriber::fmt()
    .with_env_filter(EnvFilter::from_default_env().add_directive("at_jet=debug".parse()?))
    .init();

  println!("AT-Jet Client Example");
  println!("=====================");
  println!();

  // =========================================================================
  // PART 1: Protobuf Requests (Production Format)
  // =========================================================================
  println!("=== PART 1: Protobuf Requests (Production) ===");
  println!();

  // Create JetClient for Protobuf (production)
  let client = JetClient::new("http://localhost:8080")?;

  // Test 1: Health check (plain text - not protobuf)
  println!("1. Health Check");
  let health: String = reqwest::get("http://localhost:8080/health").await?.text().await?;
  println!("   Response: {}", health);
  println!();

  // Test 2: List users (Protobuf)
  println!("2. List Users (Protobuf)");
  let users_response: ListUsersResponse = client.get("/api/users").await?;
  println!("   Total users: {}", users_response.total);
  for user in &users_response.users {
    println!("   - {} ({}): {}", user.id, user.name, user.email);
  }
  println!();

  // Test 3: Get single user (Protobuf)
  println!("3. Get User id=1 (Protobuf)");
  let user: User = client.get("/api/users/1").await?;
  println!("   ID: {}", user.id);
  println!("   Name: {}", user.name);
  println!("   Email: {}", user.email);
  println!();

  // Test 4: Create user (Protobuf)
  println!("4. Create User (Protobuf)");
  let create_request = CreateUserRequest {
    name:  "Charlie".to_string(),
    email: "charlie@example.com".to_string(),
  };
  let created_user: User = client.post("/api/users", &create_request).await?;
  println!("   Created user:");
  println!("   - ID: {}", created_user.id);
  println!("   - Name: {}", created_user.name);
  println!("   - Email: {}", created_user.email);
  println!();

  // Test 5: Delete user (Protobuf)
  println!("5. Delete User id=1 (Protobuf)");
  let delete_response: DeleteUserResponse = client.delete("/api/users/1").await?;
  println!("   Success: {}", delete_response.success);
  println!("   Message: {}", delete_response.message);
  println!();

  // =========================================================================
  // PART 2: JSON Debug Requests (Using JetClient)
  // =========================================================================
  println!("=== PART 2: JSON Debug Requests (Using JetClient) ===");
  println!();

  // Create JetClient with debug key for JSON requests
  let debug_client = JetClient::builder()
    .base_url("http://localhost:8080")
    .debug_key(DEBUG_KEY)
    .build()?;

  // Test 6: List users (JSON) - using get_json_raw for human-readable output
  println!("6. List Users (JSON raw)");
  let json_text = debug_client.get_json_raw("/api/users").await?;
  println!("   Response (human-readable):");
  println!("   {}", json_text);
  println!();

  // Test 6b: List users (JSON) - typed response
  println!("6b. List Users (JSON typed)");
  let users_json: ListUsersResponseJson = debug_client.get_json("/api/users").await?;
  println!(
    "   Total: {}, Page: {}/{}",
    users_json.total, users_json.page, users_json.page_size
  );
  for user in &users_json.users {
    println!("   - {} ({}): {}", user.id, user.name, user.email);
  }
  println!();

  // Test 7: Get single user (JSON) - type-safe with serde
  println!("7. Get User id=2 (JSON typed)");
  let user_json: UserJson = debug_client.get_json("/api/users/2").await?;
  println!("   ID: {}", user_json.id);
  println!("   Name: {}", user_json.name);
  println!("   Email: {}", user_json.email);
  println!();

  // Test 8: Create user (JSON)
  println!("8. Create User (JSON)");
  let create_request_json = CreateUserRequestJson {
    name:  "Diana".to_string(),
    email: "diana@example.com".to_string(),
  };
  let created_json: UserJson = debug_client.post_json("/api/users", &create_request_json).await?;
  println!("   Created user:");
  println!("   - ID: {}", created_json.id);
  println!("   - Name: {}", created_json.name);
  println!("   - Email: {}", created_json.email);
  println!();

  // Test 9: Delete user (JSON)
  println!("9. Delete User id=2 (JSON)");
  let delete_json: DeleteUserResponseJson = debug_client.delete_json("/api/users/2").await?;
  println!("   Success: {}", delete_json.success);
  println!("   Message: {}", delete_json.message);
  println!();

  // =========================================================================
  // PART 3: Error Cases
  // =========================================================================
  println!("=== PART 3: Error Cases ===");
  println!();

  // Test 10: Get non-existent user (Protobuf)
  println!("10. Get Non-existent User id=999 (Protobuf)");
  match client.get::<User>("/api/users/999").await {
    | Ok(user) => println!("    Found: {}", user.name),
    | Err(e) => println!("    Error (expected): {}", e),
  }
  println!();

  // Test 11: Get non-existent user (JSON)
  println!("11. Get Non-existent User id=999 (JSON)");
  match debug_client.get_json::<UserJson>("/api/users/999").await {
    | Ok(user) => println!("    Found: {}", user.name),
    | Err(e) => println!("    Error (expected): {}", e),
  }
  println!();

  println!("All tests completed!");
  println!();
  println!("Summary:");
  println!("  - JetClient::new()           → Protobuf only (production)");
  println!("  - JetClient::builder()");
  println!("      .debug_key(\"key\")");
  println!("      .build()                 → Protobuf + JSON debug support");
  println!();
  println!("  - client.get()               → Protobuf request");
  println!("  - client.get_json()          → JSON request (with debug key)");
  println!("  - client.get_json_raw()      → JSON as raw string (for inspection)");

  Ok(())
}