Parse and validate AT Protocol Lexicons with DTO generation for Laravel
at main 3.2 kB view raw
1<?php 2 3namespace SocialDept\AtpSchema\Support; 4 5use InvalidArgumentException; 6use SocialDept\AtpSchema\Contracts\DiscriminatedUnion; 7use SocialDept\AtpSchema\Data\Data; 8 9/** 10 * Helper for resolving discriminated unions based on $type field. 11 * 12 * This class uses the DiscriminatedUnion interface to build type maps 13 * and resolve union data to the correct variant class. 14 */ 15class UnionHelper 16{ 17 /** 18 * Resolve a closed union to the correct variant class. 19 * 20 * @param array $data The union data containing a $type field 21 * @param array<class-string<Data>> $variants Array of possible variant class names 22 * @return Data The resolved variant instance 23 * 24 * @throws InvalidArgumentException If $type is missing or unknown 25 */ 26 public static function resolveClosedUnion(array $data, array $variants): Data 27 { 28 // Validate $type field exists 29 if (! isset($data['$type'])) { 30 throw new InvalidArgumentException( 31 'Closed union data must contain a $type field for discrimination' 32 ); 33 } 34 35 $type = $data['$type']; 36 37 // Build type map using DiscriminatedUnion interface 38 $typeMap = static::buildTypeMap($variants); 39 40 // Check if type is known 41 if (! isset($typeMap[$type])) { 42 $knownTypes = implode(', ', array_keys($typeMap)); 43 44 throw new InvalidArgumentException( 45 "Unknown union type '{$type}'. Expected one of: {$knownTypes}" 46 ); 47 } 48 49 // Resolve to correct variant class 50 $class = $typeMap[$type]; 51 52 return $class::fromArray($data); 53 } 54 55 /** 56 * Validate an open union has $type field. 57 * 58 * Open unions pass data through as-is but must have $type for future discrimination. 59 * 60 * @param array $data The union data 61 * @return array The validated union data 62 * 63 * @throws InvalidArgumentException If $type is missing 64 */ 65 public static function validateOpenUnion(array $data): array 66 { 67 if (! isset($data['$type'])) { 68 throw new InvalidArgumentException( 69 'Open union data must contain a $type field for future discrimination' 70 ); 71 } 72 73 return $data; 74 } 75 76 /** 77 * Build a type map from variant classes using DiscriminatedUnion interface. 78 * 79 * @param array<class-string<Data>> $variants Array of variant class names 80 * @return array<string, class-string<Data>> Map of discriminator => class name 81 */ 82 protected static function buildTypeMap(array $variants): array 83 { 84 $typeMap = []; 85 86 foreach ($variants as $class) { 87 // Ensure class implements DiscriminatedUnion 88 if (! is_subclass_of($class, DiscriminatedUnion::class)) { 89 throw new InvalidArgumentException( 90 "Variant class {$class} must implement DiscriminatedUnion interface" 91 ); 92 } 93 94 // Get discriminator from the class 95 $discriminator = $class::getDiscriminator(); 96 $typeMap[$discriminator] = $class; 97 } 98 99 return $typeMap; 100 } 101}