Parse and validate AT Protocol Lexicons with DTO generation for Laravel
at dev 3.9 kB view raw
1<?php 2 3namespace SocialDept\AtpSchema\Parser; 4 5use SocialDept\AtpSchema\Data\TypeDefinition; 6use SocialDept\AtpSchema\Data\Types\ArrayType; 7use SocialDept\AtpSchema\Data\Types\BlobType; 8use SocialDept\AtpSchema\Data\Types\ObjectType; 9use SocialDept\AtpSchema\Data\Types\RefType; 10use SocialDept\AtpSchema\Data\Types\UnionType; 11use SocialDept\AtpSchema\Exceptions\TypeResolutionException; 12 13class ComplexTypeParser 14{ 15 /** 16 * Primitive parser for nested types. 17 */ 18 protected PrimitiveParser $primitiveParser; 19 20 /** 21 * Type parser for resolving references. 22 */ 23 protected ?object $typeParser = null; 24 25 /** 26 * Create a new ComplexTypeParser. 27 */ 28 public function __construct(?PrimitiveParser $primitiveParser = null) 29 { 30 $this->primitiveParser = $primitiveParser ?? new PrimitiveParser(); 31 } 32 33 /** 34 * Set the type parser for reference resolution. 35 */ 36 public function setTypeParser(object $typeParser): void 37 { 38 $this->typeParser = $typeParser; 39 } 40 41 /** 42 * Parse a complex type definition from array data. 43 * 44 * @throws TypeResolutionException 45 */ 46 public function parse(array $data): TypeDefinition 47 { 48 $type = $data['type'] ?? null; 49 50 if ($type === null) { 51 throw TypeResolutionException::unknownType('(missing type field)'); 52 } 53 54 return match ($type) { 55 'object' => $this->parseObject($data), 56 'array' => $this->parseArray($data), 57 'union' => UnionType::fromArray($data), 58 'ref' => RefType::fromArray($data), 59 'blob' => BlobType::fromArray($data), 60 default => throw TypeResolutionException::unknownType($type), 61 }; 62 } 63 64 /** 65 * Parse an object type with nested properties. 66 */ 67 protected function parseObject(array $data): ObjectType 68 { 69 $object = ObjectType::fromArray($data); 70 71 // Parse properties if present 72 if (isset($data['properties']) && is_array($data['properties'])) { 73 $properties = []; 74 75 foreach ($data['properties'] as $key => $propertyData) { 76 $properties[$key] = $this->parseNestedType($propertyData); 77 } 78 79 $object = $object->withProperties($properties); 80 } 81 82 return $object; 83 } 84 85 /** 86 * Parse an array type with nested items. 87 */ 88 protected function parseArray(array $data): ArrayType 89 { 90 $array = ArrayType::fromArray($data); 91 92 // Parse items if present 93 if (isset($data['items']) && is_array($data['items'])) { 94 $items = $this->parseNestedType($data['items']); 95 $array = $array->withItems($items); 96 } 97 98 return $array; 99 } 100 101 /** 102 * Parse a nested type definition (can be primitive or complex). 103 */ 104 protected function parseNestedType(array $data): TypeDefinition 105 { 106 $type = $data['type'] ?? null; 107 108 if ($type === null) { 109 throw TypeResolutionException::unknownType('(missing type field)'); 110 } 111 112 // Try primitive types first 113 if ($this->primitiveParser->isPrimitive($type)) { 114 return $this->primitiveParser->parse($data); 115 } 116 117 // Try complex types 118 return $this->parse($data); 119 } 120 121 /** 122 * Check if a type is a complex type. 123 */ 124 public function isComplex(string $type): bool 125 { 126 return in_array($type, [ 127 'object', 128 'array', 129 'union', 130 'ref', 131 'blob', 132 ]); 133 } 134 135 /** 136 * Get all supported complex types. 137 * 138 * @return array<string> 139 */ 140 public function getSupportedTypes(): array 141 { 142 return [ 143 'object', 144 'array', 145 'union', 146 'ref', 147 'blob', 148 ]; 149 } 150}