Parse and validate AT Protocol Lexicons with DTO generation for Laravel
at dev 2.6 kB view raw
1<?php 2 3namespace SocialDept\AtpSchema\Data\Types; 4 5use SocialDept\AtpSchema\Data\TypeDefinition; 6use SocialDept\AtpSchema\Exceptions\RecordValidationException; 7 8class UnionType extends TypeDefinition 9{ 10 /** 11 * Possible types (refs). 12 * 13 * @var array<string> 14 */ 15 public readonly array $refs; 16 17 /** 18 * Whether this is a closed union (only listed refs allowed). 19 */ 20 public readonly bool $closed; 21 22 /** 23 * Create a new UnionType. 24 * 25 * @param array<string> $refs 26 */ 27 public function __construct( 28 array $refs = [], 29 bool $closed = false, 30 ?string $description = null 31 ) { 32 parent::__construct('union', $description); 33 34 $this->refs = $refs; 35 $this->closed = $closed; 36 } 37 38 /** 39 * Create from array data. 40 */ 41 public static function fromArray(array $data): self 42 { 43 return new self( 44 refs: $data['refs'] ?? [], 45 closed: $data['closed'] ?? false, 46 description: $data['description'] ?? null 47 ); 48 } 49 50 /** 51 * Convert to array. 52 */ 53 public function toArray(): array 54 { 55 $array = ['type' => $this->type]; 56 57 if ($this->description !== null) { 58 $array['description'] = $this->description; 59 } 60 61 if (! empty($this->refs)) { 62 $array['refs'] = $this->refs; 63 } 64 65 if ($this->closed) { 66 $array['closed'] = $this->closed; 67 } 68 69 return $array; 70 } 71 72 /** 73 * Validate a value against this type definition. 74 */ 75 public function validate(mixed $value, string $path = ''): void 76 { 77 if (! is_array($value)) { 78 throw RecordValidationException::invalidType($path, 'union (object with $type)', gettype($value)); 79 } 80 81 // Union types must have a $type discriminator 82 if (! isset($value['$type'])) { 83 throw RecordValidationException::invalidValue($path, 'must contain $type property'); 84 } 85 86 $typeRef = $value['$type']; 87 88 if (! is_string($typeRef)) { 89 throw RecordValidationException::invalidValue($path, '$type must be a string'); 90 } 91 92 // If closed, validate the type is in refs 93 if ($this->closed && ! in_array($typeRef, $this->refs, true)) { 94 $allowed = implode(', ', $this->refs); 95 96 throw RecordValidationException::invalidValue($path, "type must be one of: {$allowed}"); 97 } 98 99 // Note: Actual validation of the referenced type would happen 100 // in a higher-level validator that has access to the schema repository 101 } 102}