Skip to content

Commit 257fe52

Browse files
committed
feat: documentation about rrgcli, gradle plugin and plugins api
1 parent 2dda837 commit 257fe52

File tree

4 files changed

+306
-0
lines changed

4 files changed

+306
-0
lines changed

Writerside/in.tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@
1515
<toc-element topic="Instances.md"/>
1616
<toc-element topic="ProtoBuf-Options.md"/>
1717
</toc-element>
18+
19+
<toc-element toc-title="Code generation">
20+
<toc-element toc-title="Instruments">
21+
<toc-element topic="CodeGen-Gradle.md" />
22+
<toc-element topic="CodeGen-CLI.md" />
23+
</toc-element>
24+
<toc-element toc-title="Plugins">
25+
<toc-element topic="CodeGen-Plugins.md" />
26+
</toc-element>
27+
</toc-element>
28+
1829
<toc-element topic="Compatibility-with-ProtoBuf.md"/>
1930
<toc-element topic="Implementation.md"/>
2031
<toc-element topic="Plans.md"/>

Writerside/topics/CodeGen-CLI.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# CLI
2+
The `rrgcli` tool allows you to generate code from `.proto` files for use with the rRPC framework. It supports creating
3+
Kotlin-based (and other, in future) client stubs, server stubs, data types, and metadata.
4+
5+
<note>
6+
If you're using Gradle, it's a much more convenient to use <a href="CodeGen-Gradle.md"/> for generating the code.
7+
</note>
8+
9+
## Installation
10+
11+
Download the latest release of `rrgcli` from the [rRPC GitHub Releases](https://github.com/timemates/rrpc-kotlin/releases).
12+
Once downloaded, ensure the binary is executable and optionally add it to your system's PATH:
13+
14+
```Bash
15+
chmod +x rrgcli
16+
mv rrgcli /usr/local/bin
17+
```
18+
## Usage
19+
The tool processes .proto files and outputs generated code based on the options you provide.
20+
Use the --help option to see available parameters:
21+
```
22+
Usage: rrgcli [<options>]
23+
24+
Options:
25+
--protos_input=<text> Folder with `.proto` files to be used for generation
26+
(repeatable).
27+
--permit_package_cycles=true|false
28+
Indicates whether package cycles should be ignored
29+
while parsing `.proto` files.
30+
--kotlin_output=<text> Specifies the output path for generated Kotlin files.
31+
--kotlin_client_generation=true|false
32+
Indicates whether client stubs should be generated
33+
for Kotlin. Default is `false`.
34+
--kotlin_server_generation=true|false
35+
Indicates whether server stubs should be generated
36+
for Kotlin. Default is `false`.
37+
--kotlin_type_generation=true|false
38+
Indicates whether data types should be generated for
39+
Kotlin. Default is `false`.
40+
--kotlin_metadata_generation=true|false
41+
Specifies whether metadata should be generated.
42+
--kotlin_metadata_scope_name=<text>
43+
Specifies the scope name for metadata generation. If
44+
not specified, a global instance of Metadata Container
45+
is used.
46+
-h, --help Show this message and exit.
47+
```
48+
> **Additionally about metadata generation**
49+
>
50+
> Enabling the `--kotlin_metadata_generation` flag generates debugging information about the whole schema, meaning that,
51+
> factually, it provides the same information as any generator have on generation time, but on app's runtime.
52+
> This can be helpful for servers that need details about options attached to specific levels (e.g., file-level options for a service declaration). Metadata may also provide insight into the
53+
> organization and layout of services for tooling.
54+
>
55+
>
56+
> The `--kotlin_metadata_scope_name` flag is used to make metadata scoped to a specific instance rather than placing it
57+
> in the global object. This allows you to:
58+
> - Filter out unnecessary schema information from your SchemaService.
59+
> - Avoid polluting the global scope with unrelated or redundant metadata.
60+
> - Tailor metadata for specific use cases or service groups.
61+
>
62+
> By scoping metadata, you can maintain cleaner separation of concerns, particularly in larger projects with diverse requirements.
63+
64+
65+
## Examples
66+
### Generate Client Stubs and Types
67+
This example generates Kotlin client stubs and data types from .proto files located in the protos directory:
68+
```Bash
69+
rrgcli \
70+
--protos_input=protos \
71+
--kotlin_output=build/generated \
72+
--kotlin_client_generation=true \
73+
--kotlin_type_generation=true
74+
```
75+
76+
### Handle Package Cycles and Metadata
77+
This example resolves package cycles in .proto files and generates metadata with a custom scope name:
78+
```Bash
79+
rrgcli \
80+
--protos_input=protos \
81+
--kotlin_output=build/generated \
82+
--permit_package_cycles=true \
83+
--kotlin_metadata_generation=true \
84+
--kotlin_metadata_scope_name=my_custom_scope
85+
```
86+
87+
### Multiple `.proto` Input Directories
88+
To include .proto files from multiple locations:
89+
```Bash
90+
rrgcli \
91+
--protos_input=protos1 \
92+
--protos_input=protos2 \
93+
--kotlin_output=build/generated \
94+
--kotlin_server_generation=true
95+
```
96+
_______
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Gradle Plugin
2+
The rRPC Gradle Plugin helps automate code generation for rRPC services in Gradle-based projects using `.proto` files.
3+
4+
<note>
5+
If your project is not Gradle-based, you can use the <a href="CodeGen-CLI.md">rrgcli</a> for generating code.
6+
</note>
7+
8+
## Applying the Plugin
9+
Add the plugin to your `build.gradle.kts`:
10+
```kotlin
11+
plugins {
12+
id("org.timemates.rrpc") version "$latestVersion"
13+
}
14+
```
15+
16+
<note>
17+
Make sure that you have Gradle Plugin Portal specified in your repositories.
18+
</note>
19+
20+
## Configuring the Plugin
21+
The plugin is configured using the rrpc extension, allowing customization for .proto file locations, output configurations, and generation behavior.
22+
23+
### Basic Example
24+
```Kotlin
25+
rrpc {
26+
targetSourceSet.set("commonMain") // Specify the source set for generated code
27+
protosInput.set(listOf("src/main/proto")) // Define where to find .proto files
28+
29+
kotlin {
30+
output = "build/generated/kotlin" // Specify output directory
31+
clientGeneration = true // Generate client stubs
32+
serverGeneration = true // Generate server stubs
33+
typeGeneration = true // Generate data types
34+
metadataEnabled = true // Enable metadata generation
35+
metadataScopeName = "MyScope" // Define metadata scope
36+
}
37+
}
38+
```
39+
40+
### Configuration Properties
41+
42+
#### `targetSourceSet`
43+
- **Purpose:** Specifies the source set for code generation.
44+
- **Why it's important:** Code generation tasks depend on this source set to correctly manage input and output directories for the build process. For instance, setting `commonMain` ensures that the generated files are included in the correct build phase. Especially useful for the custom source-set layouts.
45+
- **Default:** `null` (falls back to default source sets like `commonMain` or `main`).
46+
47+
```Kotlin
48+
targetSourceSet.set("commonMain")
49+
```
50+
#### `protosInput`
51+
- **Purpose**: Specifies the list of directories containing .proto files for generation.
52+
- **Default**: An empty list.
53+
54+
#### Kotlin Configuration Options
55+
56+
These options are defined under the `kotlin` block:
57+
58+
- **`output`**: Specifies the directory where Kotlin code will be generated.
59+
- **Example**:
60+
`output = "build/generated/kotlin"`
61+
- **`clientGeneration`**: Enables or disables generation of client stubs.
62+
- **Default**: `false`.
63+
- **Example**:
64+
`clientGeneration = true`
65+
- **`serverGeneration`**: Enables or disables generation of server stubs.
66+
- **Default**: `false`.
67+
- **Example**:
68+
`serverGeneration = true`
69+
- **`typeGeneration`**: Enables or disables generation of data types from `.proto` definitions.
70+
- **Default**: `false`.
71+
- **Example**:
72+
`typeGeneration = true`
73+
- **`metadataEnabled`**: Enables generation of metadata. This can help in debugging (for rRPC Inspector) or providing additional context to the server about file-level or declaration-level options.
74+
- **Default**: `false`.
75+
- **Example**:
76+
`metadataEnabled = true`
77+
- **`metadataScopeName`**: Specifies a scope for metadata, allowing you to filter or separate metadata from the global scope. Useful if you don’t want all metadata combined in a single container.
78+
- **Default**: Empty string (uses global scope).
79+
- **Example**:
80+
`metadataScopeName = "PotatoSchema"`
81+
82+
## Additional Notes
83+
- For projects with unusual directory structures or source sets, make sure to adjust targetSourceSet and protosInput accordingly.
84+
- Generated files should not be modified manually. They are regenerated during each build.
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Developing your own plugins
2+
Plugins are a powerful way to extend the functionality of a generator, enabling dynamic behavior, custom workflows or
3+
generating code for different languages. By developing your own plugin, you can tailor the generator to suit specific
4+
needs, such as processing custom inputs, providing options, or integrating with external systems.
5+
6+
This guide will walk you through the steps of creating your own plugin, covering everything from adding dependencies to
7+
handling specific signals and sending back appropriate responses. Whether you are building a plugin to modify the behavior of an existing generator or to introduce new options, this tutorial will help you get started with the Plugin API.
8+
9+
## Step 1: Add Dependencies
10+
11+
Before starting development, you need to add the necessary dependencies for the plugin to interact with the host system and exchange data. Ensure that your project includes the required libraries for communication via the Plugin API.
12+
13+
In your `build.gradle.kts` file (for Gradle projects), include the following dependencies:
14+
```Kotlin
15+
dependencies {
16+
implementation("org.timemates.rrpc:generator-core:$latestVersion")
17+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
18+
}
19+
```
20+
Ensure you use the correct version numbers according to your setup.
21+
22+
## Step 2: Define the Adapter
23+
24+
The adapter is used to map the available options and process the input received from the generator. Its purpose
25+
is to generate code, based on the input received:
26+
```Kotlin
27+
public interface SchemaAdapter {
28+
/**
29+
* List of available options for schema adapter with description. For CLI and Gradle plugin.
30+
*/
31+
public val options: List<GenerationOption>
32+
33+
/**
34+
* This method is used for generating the code usually, but can be used for other
35+
* purposes, for an example, – logging.
36+
*
37+
* @return RMResolver that might be the same as [resolver] or new in cases
38+
* when you want to modify incoming data to the following adapters.
39+
*/
40+
public fun process(
41+
options: GenerationOptions,
42+
resolver: RSResolver,
43+
): RSResolver
44+
}
45+
```
46+
For the reference, you might want to see the implementation of [Kotlin Code Generator](https://github.com/timemates/rrpc-kotlin/blob/0.7.0/generator/kotlin/src/commonMain/kotlin/org/timemates/rrpc/generator/kotlin/adapter/KotlinSchemaAdapter.kt).
47+
48+
<warning>
49+
This section is yet to be finalized.
50+
</warning>
51+
52+
Once you implemented the `SchemaAdapter` for your needs, we can continue to the setup of communication between Generator
53+
and the Plugin:
54+
55+
## Step 3: Setup Plugin Communication
56+
The next step is setting up the communication between your plugin and the generator.
57+
The plugin will send and receive signals, which are the core units of communication.
58+
59+
To begin, create an instance of the PluginCommunication class to handle the input and output streams:
60+
```Kotlin
61+
val client = PluginCommunication(
62+
System.`in`.source().buffer(), // Input stream from the generator
63+
System.out.sink().buffer() // Output stream to the generator
64+
)
65+
```
66+
67+
> [```rrcli```](CodeGen-CLI.md) as well as a Gradle Plugin, communicate with plugins through stdin/stdout. In the
68+
> example we will use java.lang.System to obtain it, but it's not locked up – we use okio for ability to target non-JVM
69+
> targets.
70+
71+
## Step 4: Handling Incoming Signals
72+
When your plugin receives a signal, it processes the signal based on its type. A plugin will always receive a G
73+
eneratorSignal.FetchOptionsList at the very beginning, which requests the available options.
74+
In response, you can send a list of options back to the generator.
75+
76+
The generator might also send a GeneratorSignal.SendInput, but it is up to the generator to decide whether this signal
77+
is needed, for example, when requesting input for certain options, such as --help.
78+
79+
Here’s an example of how to handle these signals:
80+
```Kotlin
81+
client.receive { signal ->
82+
when (signal) {
83+
GeneratorSignal.FetchOptionsList -> listOf(
84+
PluginSignal.SendOptions(
85+
schemaAdapter.options.map { it.toOptionDescriptor() }
86+
),
87+
PluginSignal.RequestInput,
88+
)
89+
is GeneratorSignal.SendInput -> {
90+
schemaAdapter.process(
91+
options = GenerationOptions(signal.args),
92+
resolver = RSResolver(signal.files),
93+
)
94+
emptyList() // No further response needed
95+
}
96+
}
97+
}
98+
```
99+
In this example:
100+
- GeneratorSignal.FetchOptionsList is always received and is responded to by sending a list of available options and potentially a RequestInput signal.
101+
- GeneratorSignal.SendInput may or may not be received, depending on the generator’s requirements.
102+
103+
## Step 5: Testing the Plugin
104+
105+
Before deploying your plugin, it’s essential to test it to ensure that it reacts correctly to signals and sends the appropriate responses. You can use unit tests or a local testing environment to simulate the interactions between your plugin and the generator.
106+
107+
<warning>
108+
This section is yet to be finalized.
109+
</warning>
110+
111+
## Conclusion
112+
113+
By following these steps, you can develop a plugin that communicates with the generator using the Plugin API. The key steps include adding dependencies, setting up communication, handling signals (particularly FetchOptionsList), and sending appropriate responses. This allows your plugin to seamlessly integrate with the generator’s functionality.
114+
115+
For more details and further examples, refer to the Plugin API documentation.

0 commit comments

Comments
 (0)