chaokunyang commented on code in PR #3723:
URL: https://github.com/apache/fory/pull/3723#discussion_r3418740841


##########
.agents/languages/dart.md:
##########
@@ -22,6 +22,8 @@ Load this file when changing `dart/`.
 - Generated struct serializers should use serializer-owned field descriptors 
for runtime resolver decisions and emit direct field-specific write/read code 
for static schemas. Do not route generated hot writes through generic 
field-info value helpers such as `writeGeneratedStructFieldInfoValue`.
 - Dart xlang or runtime ownership changes need local Dart package tests plus 
the Java-driven `DartXlangTest`; package-only smoke tests are not enough.
 - When claiming non-VM Dart support, prove a relevant non-VM compile path such 
as `dart compile js` against active runtime or example code.
+- Generated Dart gRPC service companions (`<stem>_grpc.dart`) are 
compiler-owned files that depend on the application-provided `grpc` package, 
not `dart/packages/fory`. Keep gRPC dependencies out of the Fory Dart runtime 
package.
+- Dart generated schema modules (`<Stem>ForyModule`) are the source-file 
owners. Service companions must resolve their `Fory` through that module's 
`getFory()` and must not introduce package-derived aliases or duplicate 
serializer registration paths.

Review Comment:
   This guidance encodes the same owner model that the implementation should 
move away from. If generated Dart gRPC is meant to be complete, the companion 
should own or obtain a ready Fory runtime for service payloads; putting 
`getFory()` as a required rule in `.agents` will keep future changes preserving 
the manual-install dependency. Please update or remove this rule along with the 
generator/docs so the design source does not stay stale.



##########
docs/guide/dart/grpc-support.md:
##########
@@ -0,0 +1,310 @@
+---
+title: gRPC Support
+sidebar_position: 12
+id: grpc_support
+license: |
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+---
+
+Fory can generate Dart gRPC service companions for schemas that define 
services.
+The generated code uses normal `package:grpc` clients, service bases, method
+descriptors, call options, deadlines, cancellations, and status codes, while
+request and response objects are serialized with Fory instead of protobuf.
+
+Use this mode when both RPC peers are generated from the same Fory IDL, 
protobuf
+IDL, or FlatBuffers IDL and both sides expect Fory-encoded message bodies. Use
+normal protobuf gRPC generation for APIs that must be consumed by generic
+protobuf clients, reflection tools, or components that expect protobuf message
+bytes.
+
+## Add Dependencies
+
+The `fory` package does not add gRPC dependencies. Add `grpc` (and the
+`build_runner` dev dependency that generates the Fory serializer code) in the
+application that compiles or runs generated service companions:
+
+```yaml
+dependencies:
+  fory: ^1.1.0
+  grpc: ^4.0.0
+
+dev_dependencies:
+  build_runner: ^2.4.0
+```
+
+The same dependencies cover both client and server applications.
+
+## Define a Service
+
+Service definitions can come from Fory IDL, protobuf IDL, or FlatBuffers
+`rpc_service` definitions. A Fory IDL service looks like this:
+
+```protobuf
+package demo.greeter;
+
+message HelloRequest {
+  string name = 1;
+}
+
+message HelloReply {
+  string reply = 1;
+}
+
+service Greeter {
+  rpc SayHello (HelloRequest) returns (HelloReply);
+}
+```
+
+Generate Dart model and gRPC companion code with `--grpc`:
+
+```bash
+foryc service.fdl --dart_out=./lib/generated --grpc
+```
+
+Then run `build_runner` once to emit the Fory serializer part file for the
+generated models (this step is required before the code can run):
+
+```bash
+dart run build_runner build --delete-conflicting-outputs
+```
+
+For this schema, the Dart generator emits:
+
+| File                                             | Purpose                   
                           |
+| ------------------------------------------------ | 
---------------------------------------------------- |
+| `demo/greeter/demo_greeter.dart`                 | Fory model types and the 
schema module               |
+| `demo/greeter/demo_greeter.fory.dart`            | Serializers and 
registration (built by build_runner) |
+| `demo/greeter/demo_greeter_grpc.dart`            | gRPC client, service 
base, and method descriptors    |
+| `DemoGreeterForyModule` in `demo_greeter.dart`   | Fory registration module 
for generated types         |
+| `GreeterServiceBase` in `demo_greeter_grpc.dart` | Base class for server 
implementations                |
+| `GreeterClient` in `demo_greeter_grpc.dart`      | Client stub for gRPC 
calls                           |
+
+## Register Types
+
+The generated gRPC companion serializes through the schema module's `Fory`
+instance. Create a `Fory`, install the module once at startup, and the 
generated
+client and service base pick it up automatically:
+
+```dart
+import 'package:fory/fory.dart';
+import 'demo/greeter/demo_greeter.dart';
+
+void setUpFory() {
+  DemoGreeterForyModule.install(Fory());
+}
+```
+
+`install` registers every generated type and stores the `Fory` instance that 
the
+companion's `getFory()` returns. Service implementations and clients do not
+perform manual serializer registration.
+
+## Implement a Server
+
+Extend the generated `GreeterServiceBase` and host it with grpc-dart's 
`Server`:
+
+```dart
+import 'dart:io';
+
+import 'package:grpc/grpc.dart';
+import 'demo/greeter/demo_greeter.dart';
+import 'demo/greeter/demo_greeter_grpc.dart';
+
+class GreeterService extends GreeterServiceBase {
+  @override
+  Future<HelloReply> sayHello(ServiceCall call, HelloRequest request) async {
+    final reply = HelloReply()..reply = 'Hello, ${request.name}';
+    return reply;
+  }
+}
+
+Future<void> main() async {
+  DemoGreeterForyModule.install(Fory());

Review Comment:
   These server/client snippets call `Fory()` but do not import 
`package:fory/fory.dart`, so they do not compile as written while the manual 
install remains. The preferred fix is still to remove the manual install 
requirement; if any `Fory()` construction remains in the docs, the examples 
need complete imports.



##########
docs/compiler/generated-code.md:
##########
@@ -1370,6 +1370,44 @@ void main() {
 }
 ```
 
+### gRPC Service Companions
+
+When a schema contains services and the compiler is run with `--grpc`, Dart
+generation emits one `<module>_grpc.dart` file per schema next to the model
+types. It targets `package:grpc` and serializes each request and response with
+the schema module's `getFory()` instance (for example

Review Comment:
   This compiler-level docs section repeats the manual `getFory()` runtime 
ownership model. Once Dart gRPC owns or obtains a ready runtime automatically, 
this should describe that public workflow instead of telling users service 
serialization goes through a preinstalled schema module. Otherwise the 
generated-code docs will remain inconsistent with the service design.



##########
docs/guide/dart/troubleshooting.md:
##########
@@ -138,9 +138,34 @@ dart run build_runner build --delete-conflicting-outputs
 dart test
 ```
 
+## Generated gRPC files cannot find `package:grpc` types
+
+**Cause**: gRPC packages are application dependencies. The `fory` package does
+not add gRPC as a hard dependency.
+
+**Fix**: Add `grpc` to your `pubspec.yaml` (and the `build_runner` dev
+dependency), then run `dart pub get`. See [gRPC Support](grpc-support.md).
+
+## `getFory()` throws before the first gRPC call

Review Comment:
   This troubleshooting entry turns the current startup failure into documented 
behavior. A first gRPC call should not fail because the user did not call 
`<Schema>ForyModule.install(fory)`; generated service code should be ready by 
construction or through explicit custom injection. Please remove or rewrite 
this entry when the generator ownership is fixed.



##########
compiler/fory_compiler/generators/services/dart.py:
##########
@@ -0,0 +1,283 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Dart gRPC service generator helpers."""
+
+from pathlib import Path
+from typing import List
+
+from fory_compiler.generators.base import GeneratedFile
+from fory_compiler.ir.ast import RpcMethod, Service
+
+
+class DartServiceGeneratorMixin:
+    """Generates Dart gRPC service companions for all four RPC modes."""
+
+    def generate_services(self) -> List[GeneratedFile]:
+        local_services = [
+            service
+            for service in self.schema.services
+            if not self.is_imported_type(service)
+        ]
+        if not local_services:
+            return []
+        self.check_dart_grpc_service_collisions(local_services)
+        self.check_dart_grpc_method_collisions(local_services)
+        return [self.generate_grpc_module(local_services)]
+
+    def check_dart_grpc_service_collisions(self, services: List[Service]) -> 
None:
+        generated_names = set(self._top_level_names())
+        service_names = set()
+        for service in services:
+            for emitted in (f"{service.name}Client", 
f"{service.name}ServiceBase"):
+                if emitted in generated_names or emitted in service_names:
+                    raise ValueError(
+                        f"Dart gRPC class {emitted} conflicts with a generated 
"
+                        "type or another service; rename the service or type"
+                    )
+                service_names.add(emitted)
+
+    def check_dart_grpc_method_collisions(self, services: List[Service]) -> 
None:

Review Comment:
   This only checks method-vs-method collisions after camelCasing. The 
generated members also sit on classes extending `Client` and `Service`, and 
every Dart object inherits `Object`, so valid RPC names like `ToString` or 
`HashCode` generate invalid overrides or conflicting members (`toString(...)`, 
`hashCode(...)`). Please reserve those inherited/member surfaces or fail 
codegen with a clear diagnostic, and add analyzer coverage for those names.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to