diff --git a/src/Plugin/Generator/GrpcGenerator.php b/src/Plugin/Generator/GrpcGenerator.php index ec2e5db..72c4bf9 100644 --- a/src/Plugin/Generator/GrpcGenerator.php +++ b/src/Plugin/Generator/GrpcGenerator.php @@ -34,8 +34,15 @@ public function generateClient(Parser\ServiceDescriptor $service): PhpNamespace $classType = new ClassType($name) ->setFinal() ->setReadOnly() - ->addComment('@api') - ->addComment($service->comment !== null ? "\n{$service->comment}" : ''); + ->addComment('@api'); + + if ($service->options?->deprecated === true) { + $classType = $classType->addComment('@deprecated'); + } + + if ($service->comment !== null) { + $classType = $classType->addComment("\n{$service->comment}"); + } $namespace->add($classType); @@ -59,6 +66,12 @@ public function generateClient(Parser\ServiceDescriptor $service): PhpNamespace ->addMethod(Naming::camelCase($method->name)) ->setPublic(); + if ($method->options?->deprecated === true) { + $namespace->addUse('Deprecated'); + + $classMethod = $classMethod->addAttribute('Deprecated'); + } + if ($method->comment !== null) { $classMethod->addComment((string) $method->comment); } @@ -198,8 +211,15 @@ public function generateServer(Parser\ServiceDescriptor $service): PhpNamespace $namespace = $this->namespacer->create($service->path); $interfaceType = new InterfaceType(\sprintf('%sServer', Naming::pascalCase($service->name))) - ->addComment('@api') - ->addComment($service->comment !== null ? "\n{$service->comment}" : ''); + ->addComment('@api'); + + if ($service->options?->deprecated === true) { + $interfaceType = $interfaceType->addComment('@deprecated'); + } + + if ($service->comment !== null) { + $interfaceType = $interfaceType->addComment("\n{$service->comment}"); + } $namespace->add($interfaceType); @@ -224,6 +244,12 @@ public function generateServer(Parser\ServiceDescriptor $service): PhpNamespace ->addMethod(Naming::camelCase($method->name)) ->setPublic(); + if ($method->options?->deprecated === true) { + $namespace->addUse('Deprecated'); + + $interfaceMethod = $interfaceMethod->addAttribute('Deprecated'); + } + if ($method->comment !== null) { $interfaceMethod->addComment((string) $method->comment); } diff --git a/src/Plugin/Generator/ProtoGenerator.php b/src/Plugin/Generator/ProtoGenerator.php index 24f3255..ebb3ef0 100644 --- a/src/Plugin/Generator/ProtoGenerator.php +++ b/src/Plugin/Generator/ProtoGenerator.php @@ -54,15 +54,23 @@ public function generateEnum(Parser\EnumDescriptor $enum): PhpNamespace )); $enumType = new EnumType($enumName) - ->addComment('@api') - ->addComment($enum->comment !== null ? "\n{$enum->comment}" : '') ->setType('int') ->setCases(array_map( static fn(Parser\EnumCaseDescriptor $case) => new EnumCase(Naming::secureEnumCase($case->name)) ->setValue($case->value) - ->setComment((string) $case->comment), + ->addComment(($deprecated = $case->options?->deprecated) === true ? '@deprecated' : '') + ->addComment(($deprecated === true ? "\n" : '') . (string) $case->comment), $enum->cases, - )); + )) + ->addComment('@api'); + + if ($enum->options?->deprecated === true) { + $enumType = $enumType->addComment('@deprecated'); + } + + if ($enum->comment !== null) { + $enumType = $enumType->addComment("\n{$enum->comment}"); + } $namespace->add($enumType); @@ -127,9 +135,17 @@ private function generateMessage(PhpNamespace $namespace, string $className, Par $classType = new ClassType($className) ->setFinal() ->setReadOnly() - ->setImplements($implements) - ->addComment('@api') - ->addComment($message->comment !== null ? "\n{$message->comment}" : ''); + ->setImplements($implements); + + $classType = $classType->addComment('@api'); + + if ($message->options?->deprecated === true) { + $classType = $classType->addComment('@deprecated'); + } + + if ($message->comment !== null) { + $classType = $classType->addComment("\n{$message->comment}"); + } $namespace->add($classType); @@ -226,6 +242,10 @@ private function generateMessage(PhpNamespace $namespace, string $className, Par ->setNullable($nullable) ->setDefaultValue($repeated ? [] : $default); + if ($field->options?->deprecated === true) { + $parameter->addComment('@deprecated'); + } + if ($repeated || $field->comment !== null || $type->isMap) { $docType = $type->resolvedType(); diff --git a/src/Plugin/Parser.php b/src/Plugin/Parser.php index e93280e..78c893a 100644 --- a/src/Plugin/Parser.php +++ b/src/Plugin/Parser.php @@ -245,6 +245,7 @@ private static function parseServices(array $descriptors, CommentExtractor $comm $name, self::parseServiceMethods($descriptor->method, $comments->clone($commentPath)), $comments->extract($commentPath), + $descriptor->options, ); } @@ -273,6 +274,7 @@ private static function parseServiceMethods( clientStreaming: $descriptor->clientStreaming ?? false, serverStreaming: $descriptor->serverStreaming ?? false, comment: $comments->extract(\sprintf('%d.%d', Comments::SERVICE_METHOD_COMMENT_PATH, $idx)), + options: $descriptor->options, ); } diff --git a/src/Plugin/Parser/ServiceDescriptor.php b/src/Plugin/Parser/ServiceDescriptor.php index 9ed62d7..6c417da 100644 --- a/src/Plugin/Parser/ServiceDescriptor.php +++ b/src/Plugin/Parser/ServiceDescriptor.php @@ -4,6 +4,7 @@ namespace Thesis\Protoc\Plugin\Parser; +use Google\Protobuf\ServiceOptions; use Thesis\Protoc\Plugin\Comment; /** @@ -19,5 +20,6 @@ public function __construct( public string $path, public array $methods, public ?Comment $comment = null, + public ?ServiceOptions $options = null, ) {} } diff --git a/src/Plugin/Parser/ServiceMethodDescriptor.php b/src/Plugin/Parser/ServiceMethodDescriptor.php index f67ac23..e580c6f 100644 --- a/src/Plugin/Parser/ServiceMethodDescriptor.php +++ b/src/Plugin/Parser/ServiceMethodDescriptor.php @@ -4,6 +4,7 @@ namespace Thesis\Protoc\Plugin\Parser; +use Google\Protobuf\MethodOptions; use Thesis\Protoc\Plugin\Comment; /** @@ -20,6 +21,7 @@ public function __construct( public bool $clientStreaming, public bool $serverStreaming, public ?Comment $comment = null, + public ?MethodOptions $options = null, ) { $this->bidirectionalStreaming = $this->clientStreaming && $this->serverStreaming; } diff --git a/tests/fixtures/deprecated/deprecated.proto b/tests/fixtures/deprecated/deprecated.proto new file mode 100644 index 0000000..ac8b4b3 --- /dev/null +++ b/tests/fixtures/deprecated/deprecated.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +package test.api.v1; + +// Type is deprecated. +enum Type { + option deprecated = true; + + // Case is deprecated. + TYPE_UNSPECIFIED = 0 [deprecated = true]; + TYPE_PROTO3 = 1; +} + +// Request is deprecated. +message Request { + option deprecated = true; + + // Field is deprecated. + string name = 1 [deprecated = true]; + + // Nested message is also deprecated. + message NestedRequest { + option deprecated = true; + + // Nested field is deprecated too. + string name = 1 [deprecated = true]; + } +} + +// Service is deprecated. +service EchoService { + option deprecated = true; + + // Rpc is also deprecated. + rpc Echo(Request) returns (Request) { + option deprecated = true; + }; +} diff --git a/tests/snapshots/deprecated/Test/Api/V1/DeprecatedDescriptorRegistry.php b/tests/snapshots/deprecated/Test/Api/V1/DeprecatedDescriptorRegistry.php new file mode 100644 index 0000000..56ff089 --- /dev/null +++ b/tests/snapshots/deprecated/Test/Api/V1/DeprecatedDescriptorRegistry.php @@ -0,0 +1,48 @@ +add(Registry\Descriptor::base64(self::DESCRIPTOR_BUFFER), new File( + name: 'deprecated.proto', + messages: [ + new File\MessageDescriptor('test.api.v1.Request', \Test\Api\V1\Request::class), + new File\MessageDescriptor('test.api.v1.Request.NestedRequest', \Test\Api\V1\Request\NestedRequest::class), + ], + enums: [ + new File\EnumDescriptor('test.api.v1.Type', \Test\Api\V1\Type::class), + ], + services: [ + new File\ServiceDescriptor( + name: 'test.api.v1.EchoService', + methods: [ + new File\MethodDescriptor('Echo', false, false), + ], + ), + ], + )); + } +} diff --git a/tests/snapshots/deprecated/Test/Api/V1/EchoServiceClient.php b/tests/snapshots/deprecated/Test/Api/V1/EchoServiceClient.php new file mode 100644 index 0000000..0b17193 --- /dev/null +++ b/tests/snapshots/deprecated/Test/Api/V1/EchoServiceClient.php @@ -0,0 +1,59 @@ + $invoke */ + $invoke = new Client\Invoke( + method: '/test.api.v1.EchoService/Echo', + type: \Test\Api\V1\Request::class, + ); + + return $this->client->invoke( + request: $request, + invoke: $invoke, + md: $md, + cancellation: $cancellation, + ); + } +} diff --git a/tests/snapshots/deprecated/Test/Api/V1/EchoServiceServer.php b/tests/snapshots/deprecated/Test/Api/V1/EchoServiceServer.php new file mode 100644 index 0000000..d59b564 --- /dev/null +++ b/tests/snapshots/deprecated/Test/Api/V1/EchoServiceServer.php @@ -0,0 +1,36 @@ +server->echo(...)), + Server\RpcType::Unary, + ), + ]); + } +} diff --git a/tests/snapshots/deprecated/Test/Api/V1/Request.php b/tests/snapshots/deprecated/Test/Api/V1/Request.php new file mode 100644 index 0000000..c9e85a9 --- /dev/null +++ b/tests/snapshots/deprecated/Test/Api/V1/Request.php @@ -0,0 +1,33 @@ +register( + new \Thesis\Protobuf\Registry\OnceRegistrar(new \Test\Api\V1\DeprecatedDescriptorRegistry()), +);