# sqlx-record Lookup Tables Skill Guide to lookup_table! and lookup_options! macros. ## Triggers - "lookup table", "lookup options" - "code enum", "status enum" - "type-safe codes", "constants" ## lookup_table! Macro Creates a database-backed lookup entity with type-safe code enum. ### Syntax ```rust lookup_table!(Name, "code1", "code2", "code3"); ``` ### Generated Code ```rust // Entity struct with CRUD via #[derive(Entity)] #[derive(Entity, FromRow)] pub struct Name { #[primary_key] pub code: String, pub name: String, pub description: String, pub is_active: bool, } // Type-safe enum pub enum NameCode { Code1, Code2, Code3, } // String constants impl Name { pub const CODE1: &'static str = "code1"; pub const CODE2: &'static str = "code2"; pub const CODE3: &'static str = "code3"; } // Display impl (enum -> string) impl Display for NameCode { ... } // TryFrom impl (string -> enum) impl TryFrom<&str> for NameCode { ... } ``` ### Example ```rust lookup_table!(OrderStatus, "pending", "processing", "shipped", "delivered", "cancelled" ); // Usage let status = OrderStatus::PENDING; // "pending" let code = OrderStatusCode::Pending; println!("{}", code); // "pending" // Parse from string let code = OrderStatusCode::try_from("shipped")?; // Query the lookup table let statuses = OrderStatus::find(&pool, filters![("is_active", true)], None).await?; // Get specific status let status = OrderStatus::get_by_code(&pool, OrderStatus::PENDING).await?; ``` ### Database Schema ```sql CREATE TABLE order_status ( code VARCHAR(255) PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT, is_active BOOLEAN NOT NULL DEFAULT TRUE ); INSERT INTO order_status VALUES ('pending', 'Pending', 'Order awaiting processing', TRUE), ('processing', 'Processing', 'Order being prepared', TRUE), ('shipped', 'Shipped', 'Order in transit', TRUE), ('delivered', 'Delivered', 'Order completed', TRUE), ('cancelled', 'Cancelled', 'Order cancelled', TRUE); ``` ## lookup_options! Macro Creates code enum without database entity (for embedded options). ### Syntax ```rust lookup_options!(Name, "code1", "code2", "code3"); ``` ### Generated Code ```rust // Type-safe enum pub enum NameCode { Code1, Code2, Code3, } // Unit struct for constants pub struct Name; impl Name { pub const CODE1: &'static str = "code1"; pub const CODE2: &'static str = "code2"; pub const CODE3: &'static str = "code3"; } // Display and TryFrom implementations ``` ### Example ```rust lookup_options!(PaymentMethod, "credit-card", "debit-card", "paypal", "bank-transfer", "crypto" ); // Usage let method = PaymentMethod::CREDIT_CARD; // "credit-card" // In entity #[derive(Entity)] struct Order { #[primary_key] id: Uuid, payment_method: String, // Stores the code string } // Query with constant let orders = Order::find(&pool, filters![("payment_method", PaymentMethod::PAYPAL)], None ).await?; // Type-safe matching match PaymentMethodCode::try_from(order.payment_method.as_str())? { PaymentMethodCode::CreditCard => process_credit_card(), PaymentMethodCode::DebitCard => process_debit_card(), PaymentMethodCode::Paypal => process_paypal(), PaymentMethodCode::BankTransfer => process_bank_transfer(), PaymentMethodCode::Crypto => process_crypto(), } ``` ## Code Naming Conventions Codes are converted to enum variants using camelCase: | Code String | Enum Variant | Constant | |-------------|--------------|----------| | `"active"` | `Active` | `ACTIVE` | | `"pro-rata"` | `ProRata` | `PRO_RATA` | | `"full-24-hours"` | `Full24Hours` | `FULL_24_HOURS` | | `"activity_added"` | `ActivityAdded` | `ACTIVITY_ADDED` | | `"no-refunds"` | `NoRefunds` | `NO_REFUNDS` | ## When to Use Each ### Use lookup_table! when: - Lookup values are stored in database - Values may change at runtime - Need to query/manage lookup values - Want audit trail on lookup changes ### Use lookup_options! when: - Codes are compile-time constants - No database table needed - Codes are embedded in other entities - Values won't change without code deployment ## Real-World Examples ### Status Workflows ```rust lookup_table!(TaskStatus, "todo", "in-progress", "review", "done", "archived" ); impl Task { pub fn can_transition(&self, to: TaskStatusCode) -> bool { match (TaskStatusCode::try_from(self.status.as_str()), to) { (Ok(TaskStatusCode::Todo), TaskStatusCode::InProgress) => true, (Ok(TaskStatusCode::InProgress), TaskStatusCode::Review) => true, (Ok(TaskStatusCode::Review), TaskStatusCode::Done) => true, (Ok(TaskStatusCode::Review), TaskStatusCode::InProgress) => true, (Ok(_), TaskStatusCode::Archived) => true, _ => false, } } } ``` ### Configuration Options ```rust lookup_options!(LogLevel, "debug", "info", "warn", "error"); lookup_options!(Theme, "light", "dark", "system"); lookup_options!(Language, "en", "es", "fr", "de", "ja"); #[derive(Entity)] struct UserPreferences { #[primary_key] user_id: Uuid, log_level: String, theme: String, language: String, } // Usage let prefs = UserPreferences { user_id: user_id, log_level: LogLevel::INFO.into(), theme: Theme::DARK.into(), language: Language::EN.into(), }; ``` ### Domain-Specific Types ```rust lookup_options!(FastingPattern, "time-restricted", "omad", "alternate-day", "five-two", "extended", "custom" ); lookup_options!(ProgramType, "self-paced", "live", "challenge", "maintenance" ); lookup_options!(SubscriptionTier, "free", "basic", "premium", "unlimited" ); ``` ## Error Handling ```rust // TryFrom returns Result match UserStatusCode::try_from(unknown_string) { Ok(code) => handle_status(code), Err(msg) => { // msg: "Unknown userstatus code: invalid_value" log::warn!("{}", msg); handle_unknown_status() } } // Or use unwrap_or for default let code = UserStatusCode::try_from(status_str) .unwrap_or(UserStatusCode::Pending); ```