|
| 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