Skip to content

Commit

Permalink
Merge pull request #52 from samtkit/generator-register
Browse files Browse the repository at this point in the history
Add API to register generators
  • Loading branch information
KCreate authored Jun 15, 2023
2 parents ec49d95 + 71a62ba commit 986dc0d
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 27 deletions.
17 changes: 13 additions & 4 deletions cli/src/main/kotlin/tools/samt/cli/CliCompiler.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tools.samt.cli

import tools.samt.codegen.Codegen
import tools.samt.codegen.http.HttpTransportConfigurationParser
import tools.samt.common.DiagnosticController
import tools.samt.common.DiagnosticException
import tools.samt.common.collectSamtFiles
Expand All @@ -11,6 +12,9 @@ import tools.samt.semantic.SemanticModel
import java.io.IOException
import kotlin.io.path.isDirectory
import kotlin.io.path.notExists
import tools.samt.codegen.kotlin.KotlinTypesGenerator
import tools.samt.codegen.kotlin.ktor.KotlinKtorConsumerGenerator
import tools.samt.codegen.kotlin.ktor.KotlinKtorProviderGenerator

internal fun compile(command: CompileCommand, controller: DiagnosticController) {
val (configuration ,_) = CliConfigParser.readConfig(command.file, controller) ?: return
Expand Down Expand Up @@ -65,12 +69,17 @@ internal fun compile(command: CompileCommand, controller: DiagnosticController)
return
}

for (generator in configuration.generators) {
val files = Codegen.generate(model, generator, controller)
Codegen.registerGenerator(KotlinTypesGenerator)
Codegen.registerGenerator(KotlinKtorProviderGenerator)
Codegen.registerGenerator(KotlinKtorConsumerGenerator)
Codegen.registerTransportParser(HttpTransportConfigurationParser)

for (generatorConfig in configuration.generators) {
val files = Codegen.generate(model, generatorConfig, controller)
try {
OutputWriter.write(generator.output, files)
OutputWriter.write(generatorConfig.output, files)
} catch (e: IOException) {
controller.reportGlobalError("Failed to write output for generator '${generator.name}': ${e.message}")
controller.reportGlobalError("Failed to write output for generator '${generatorConfig.name}': ${e.message}")
}
}
}
39 changes: 27 additions & 12 deletions codegen/src/main/kotlin/tools/samt/codegen/Codegen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,33 @@ import tools.samt.api.plugin.Generator
import tools.samt.api.plugin.GeneratorParams
import tools.samt.api.plugin.TransportConfigurationParser
import tools.samt.api.types.SamtPackage
import tools.samt.codegen.http.HttpTransportConfigurationParser
import tools.samt.codegen.kotlin.KotlinTypesGenerator
import tools.samt.codegen.kotlin.ktor.KotlinKtorConsumerGenerator
import tools.samt.codegen.kotlin.ktor.KotlinKtorProviderGenerator
import tools.samt.common.DiagnosticController
import tools.samt.common.SamtGeneratorConfiguration
import tools.samt.semantic.SemanticModel

object Codegen {
private val generators: List<Generator> = listOf(
KotlinTypesGenerator,
KotlinKtorProviderGenerator,
KotlinKtorConsumerGenerator,
)
private val generators: MutableList<Generator> = mutableListOf()

private val transports: List<TransportConfigurationParser> = listOf(
HttpTransportConfigurationParser,
)
/**
* Register the [generator] to be used when generating code.
* The generator is called when a user configures it within their `samt.yaml` configuration, where the generator is referenced by its [Generator.name].
* Only generators registered here will be considered when calling [generate].
*/
@JvmStatic
fun registerGenerator(generator: Generator) {
generators += generator
}

private val transports: MutableList<TransportConfigurationParser> = mutableListOf()

/**
* Register the [parser] as a transport configuration, which will be used to parse the `transport` section of a SAMT provider.
* Only transport configurations registered here will be considered when calling [generate].
*/
@JvmStatic
fun registerTransportParser(parser: TransportConfigurationParser) {
transports += parser
}

internal class SamtGeneratorParams(
semanticModel: SemanticModel,
Expand All @@ -45,6 +54,12 @@ object Codegen {
}
}

/**
* Run the appropriate generator for the given [configuration], using the given [semanticModel].
* To ensure binary compatibility, the types within the [semanticModel] will be mapped to their [tools.samt.api] equivalents.
* @return a list of generated files
*/
@JvmStatic
fun generate(
semanticModel: SemanticModel,
configuration: SamtGeneratorConfiguration,
Expand Down
7 changes: 5 additions & 2 deletions codegen/src/main/kotlin/tools/samt/codegen/PublicApiMapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ class PublicApiMapper(
else -> controller.reportGlobalError("Multiple transport configuration parsers found for transport '$name'")
}

return object : TransportConfiguration {}
return object : TransportConfiguration {
override val name: String
get() = this@toPublicTransport.name
}
}

private fun tools.samt.semantic.ProviderType.Implements.toPublicImplements() = object : ProvidedService {
Expand All @@ -169,7 +172,7 @@ class PublicApiMapper(
override val name get() = this@toPublicAlias.name
override val qualifiedName by unsafeLazy { this@toPublicAlias.getQualifiedName() }
override val aliasedType by unsafeLazy { this@toPublicAlias.aliasedType.toPublicTypeReference() }
override val fullyResolvedType by unsafeLazy { this@toPublicAlias.fullyResolvedType.toPublicTypeReference() }
override val runtimeType by unsafeLazy { this@toPublicAlias.fullyResolvedType.toPublicTypeReference() }
}

private inline fun <reified T : tools.samt.semantic.ResolvedTypeReference.Constraint> List<tools.samt.semantic.ResolvedTypeReference.Constraint>.findConstraint() =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import tools.samt.api.transports.http.SerializationMode
import tools.samt.api.transports.http.TransportMode

object HttpTransportConfigurationParser : TransportConfigurationParser {
override val transportName: String
get() = "http"
override val transportName: String get() = HttpTransportConfiguration.name

private val isValidRegex = Regex("""\w+\s+\S+(\s+\{.*?\s+in\s+\S+})*""")
private val methodEndpointRegex = Regex("""(\w+)\s+(\S+)(.*)""")
Expand Down Expand Up @@ -212,6 +211,8 @@ class HttpTransportConfiguration(
override val serializationMode: SerializationMode,
val services: List<ServiceConfiguration>,
) : SamtHttpTransport {
override val name: String = HttpTransportConfiguration.name

class ServiceConfiguration(
val name: String,
val path: String,
Expand Down Expand Up @@ -284,4 +285,8 @@ class HttpTransportConfiguration(
TransportMode.Body
}
}

companion object {
const val name = "http"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,16 @@ private fun StringBuilder.appendDecodeRecordField(field: RecordField, options: M
private fun StringBuilder.appendEncodeAlias(alias: AliasType, options: Map<String, String>) {
appendLine("/** Encode alias ${alias.qualifiedName} to JSON */")
appendLine("fun `encode ${alias.name}`(value: ${alias.getQualifiedName(options)}): JsonElement =")
appendLine(" ${encodeJsonElement(alias.fullyResolvedType, options, valueName = "value")}")
appendLine(" ${encodeJsonElement(alias.runtimeType, options, valueName = "value")}")
}

private fun StringBuilder.appendDecodeAlias(alias: AliasType, options: Map<String, String>) {
appendLine("/** Decode alias ${alias.qualifiedName} from JSON */")
appendLine("fun `decode ${alias.name}`(json: JsonElement): ${alias.fullyResolvedType.getQualifiedName(options)} {")
if (alias.fullyResolvedType.isRuntimeOptional) {
appendLine("fun `decode ${alias.name}`(json: JsonElement): ${alias.runtimeType.getQualifiedName(options)} {")
if (alias.runtimeType.isRuntimeOptional) {
appendLine(" if (json is JsonNull) return null")
}
appendLine(" return ${decodeJsonElement(alias.fullyResolvedType, options, valueName = "json")}")
appendLine(" return ${decodeJsonElement(alias.runtimeType, options, valueName = "json")}")
appendLine("}")
}

Expand Down
9 changes: 9 additions & 0 deletions codegen/src/test/kotlin/tools/samt/codegen/CodegenTest.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tools.samt.codegen

import tools.samt.api.plugin.CodegenFile
import tools.samt.codegen.http.HttpTransportConfigurationParser
import tools.samt.common.DiagnosticController
import tools.samt.common.collectSamtFiles
import tools.samt.common.readSamtSource
Expand All @@ -13,6 +14,9 @@ import kotlin.io.path.Path
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import tools.samt.codegen.kotlin.KotlinTypesGenerator
import tools.samt.codegen.kotlin.ktor.KotlinKtorConsumerGenerator
import tools.samt.codegen.kotlin.ktor.KotlinKtorProviderGenerator

class CodegenTest {
private val testDirectory = Path("src/test/resources/generator-test-model")
Expand Down Expand Up @@ -43,6 +47,11 @@ class CodegenTest {

assertFalse(controller.hasErrors())

Codegen.registerGenerator(KotlinTypesGenerator)
Codegen.registerGenerator(KotlinKtorProviderGenerator)
Codegen.registerGenerator(KotlinKtorConsumerGenerator)
Codegen.registerTransportParser(HttpTransportConfigurationParser)

val actualFiles = mutableListOf<CodegenFile>()
for (generator in configuration.generators) {
actualFiles += Codegen.generate(model, generator, controller).map { it.copy(filepath = generator.output.resolve(it.filepath).toString()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ interface TransportConfigurationParser {
* A base interface for transport configurations.
* This interface is intended to be sub-typed and extended by transport configuration implementations.
*/
interface TransportConfiguration
interface TransportConfiguration {
/**
* The name of this transport protocol.
* Can be used to display to a user.
*/
val name: String
}

/**
* The parameters for a [TransportConfigurationParser].
Expand Down
4 changes: 2 additions & 2 deletions public-api/src/main/kotlin/tools/samt/api/types/UserTypes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ interface AliasType : UserType {
val aliasedType: TypeReference

/**
* The fully resolved type, will not contain any type aliases anymore, just the underlying merged type
* The runtime type, which will not contain any type aliases, just the underlying merged type
*/
val fullyResolvedType: TypeReference
val runtimeType: TypeReference
}

/**
Expand Down

0 comments on commit 986dc0d

Please sign in to comment.