Skip to content

Writing kdocs+tests for generateCode.kt #1311

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,89 @@
package org.jetbrains.kotlinx.dataframe.api

import org.jetbrains.kotlinx.dataframe.DataFrame
import org.jetbrains.kotlinx.dataframe.annotations.ColumnName
import org.jetbrains.kotlinx.dataframe.annotations.DataSchema
import org.jetbrains.kotlinx.dataframe.codeGen.CodeGenerator
import org.jetbrains.kotlinx.dataframe.codeGen.MarkerVisibility
import org.jetbrains.kotlinx.dataframe.codeGen.NameNormalizer
import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup
import org.jetbrains.kotlinx.dataframe.columns.FrameColumn
import org.jetbrains.kotlinx.dataframe.documentation.DocumentationUrls
import org.jetbrains.kotlinx.dataframe.documentation.ExcludeFromSources
import org.jetbrains.kotlinx.dataframe.impl.codeGen.from
import org.jetbrains.kotlinx.dataframe.schema.DataFrameSchema

// region Docs

/**
* Generates a [CodeString] containing generated [@DataSchema][DataSchema] $[TYPES]
* for the given $[RECEIVER]
* (including all nested [frame columns][FrameColumn] and [column groups][ColumnGroup]).
*
* These generated declarations can also be called "markers".
*
* This is useful when working with the compiler plugin in cases where the schema
* cannot be inferred automatically from the source.
*
* This function is a simplified wrapper for the more advanced and customizable
* [CodeGenerator] API.
* For more customizability, have a look at [CodeGenerator.create()][CodeGenerator.create].
*
* For more information: {@include [DocumentationUrls.DataSchemaGeneration]}
*
* @return [CodeString] – A value class wrapper for [String], containing
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and CodeString.print() utility :)

* the generated Kotlin code of data schema declarations (markers).
*/
@ExcludeFromSources
private interface CommonGenerateCodeDocs {

// "interfaces" or "data classes"
interface TYPES

// "DataFrameSchema" or "DataFrame"
interface RECEIVER
}

@ExcludeFromSources
private interface Params {

/** @param markerName The base name to use for generated data schema declarations (markers). */
interface MarkerName

/** @include [MarkerName] If not specified, generates a name from type [T]. */
interface MarkerNameOptional

/** @param fields Whether to generate fields (`val ...:`) inside the generated data schema declarations (markers). */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How to put it.. Technically field = false, extensions = true is enough to refer to columns in dataframe
But plugin won't be able to extract schema from such dataschema. At least we should state it, at best maybe even remove this option?

interface Fields

/** @param extensionProperties Whether to generate extension properties in addition to data schema declarations (markers). */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we should say that if either compiler plugin or ksp are used in the project, extension properties will conflict with generated ones.
Soo, same as with fields, does it even make sense to have this option..

interface ExtensionProperties

/** @param visibility Visibility modifier for the generated declarations (markers). */
interface Visibility
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To add some context, this argument was added to generate valid code for https://kotlinlang.org/docs/api-guidelines-simplicity.html#use-explicit-api-mode
Maybe it's a useful info, maybe not :)


/** @param useFqNames If `true`, fully qualified type names will be used in generated code. */
interface UseFqNames

/**
* @param nameNormalizer Strategy for converting column names (with spaces, underscores, etc.) to valid Kotlin identifiers.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to use word "Kotlin-style identifiers" for default normalizer
And
"Generated properties will still refer to columns by their actual name via @ColumnName annotation`

* Columns will keep their original name inside the dataframe via [@ColumnName][ColumnName].
*/
interface NameNormalizer
}

// endregion

// region DataFrame

/**
* @include [CommonGenerateCodeDocs]
* {@set [CommonGenerateCodeDocs.RECEIVER] [DataFrame's][this] [schema][DataFrameSchema]}
* {@set [CommonGenerateCodeDocs.TYPES] interfaces and extension properties}
*
* @include [Params.Fields] Default is `true`.
* @include [Params.ExtensionProperties] Default is `true`.
*/
public inline fun <reified T> DataFrame<T>.generateCode(
fields: Boolean = true,
extensionProperties: Boolean = true,
Expand All @@ -19,6 +94,16 @@ public inline fun <reified T> DataFrame<T>.generateCode(
extensionProperties = extensionProperties,
)

/**
* @include [CommonGenerateCodeDocs]
* {@set [CommonGenerateCodeDocs.RECEIVER] [DataFrame's][this] [schema][DataFrameSchema]}
* {@set [CommonGenerateCodeDocs.TYPES] interfaces and extension properties}
*
* @include [Params.MarkerName]
* @include [Params.Fields] Default is `true`.
* @include [Params.ExtensionProperties] Default is `true`.
* @include [Params.Visibility] Default is [MarkerVisibility.IMPLICIT_PUBLIC].
*/
public fun <T> DataFrame<T>.generateCode(
markerName: String,
fields: Boolean = true,
Expand All @@ -32,11 +117,39 @@ public fun <T> DataFrame<T>.generateCode(
visibility = visibility,
)

/**
* @include [CommonGenerateCodeDocs]
* {@set [CommonGenerateCodeDocs.RECEIVER] [DataFrame's][this] [schema][DataFrameSchema]}
* {@set [CommonGenerateCodeDocs.TYPES] interfaces}
*
* @include [Params.MarkerNameOptional]
*/
public inline fun <reified T> DataFrame<T>.generateInterfaces(): CodeString =
schema().generateInterfaces(
markerName = markerName<T>(),
)

/**
* @include [CommonGenerateCodeDocs]
* {@set [CommonGenerateCodeDocs.RECEIVER] [DataFrame's][this] [schema][DataFrameSchema]}
* {@set [CommonGenerateCodeDocs.TYPES] interfaces}
*
* @include [Params.MarkerNameOptional]
*/
public fun <T> DataFrame<T>.generateInterfaces(markerName: String): CodeString =
schema().generateInterfaces(markerName = markerName)

/**
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please mention that data classes can be used with "toListOf"

* @include [CommonGenerateCodeDocs]
* {@set [CommonGenerateCodeDocs.RECEIVER] [DataFrame's][this] [schema][DataFrameSchema]}
* {@set [CommonGenerateCodeDocs.TYPES] data classes}
*
* @include [Params.MarkerNameOptional]
* @include [Params.ExtensionProperties] Default is `false`.
* @include [Params.Visibility] Default is [MarkerVisibility.IMPLICIT_PUBLIC].
* @include [Params.UseFqNames] Default is `false`.
* @include [Params.NameNormalizer] Default is [NameNormalizer.default][NameNormalizer.Companion.default].
*/
public inline fun <reified T> DataFrame<T>.generateDataClasses(
markerName: String? = null,
extensionProperties: Boolean = false,
Expand All @@ -45,20 +158,27 @@ public inline fun <reified T> DataFrame<T>.generateDataClasses(
nameNormalizer: NameNormalizer = NameNormalizer.default,
): CodeString =
schema().generateDataClasses(
name = markerName ?: markerName<T>(),
markerName = markerName ?: markerName<T>(),
extensionProperties = extensionProperties,
visibility = visibility,
useFqNames = useFqNames,
nameNormalizer = nameNormalizer,
)

public fun <T> DataFrame<T>.generateInterfaces(markerName: String): CodeString =
schema().generateInterfaces(markerName = markerName)

// endregion

// region DataFrameSchema

/**
* @include [CommonGenerateCodeDocs]
* {@set [CommonGenerateCodeDocs.RECEIVER] [DataFrameSchema][this]}
* {@set [CommonGenerateCodeDocs.TYPES] interfaces and extension properties}
*
* @include [Params.MarkerName]
* @include [Params.Fields] Default is `true`.
* @include [Params.ExtensionProperties] Default is `true`.
* @include [Params.Visibility] Default is [MarkerVisibility.IMPLICIT_PUBLIC].
*/
@JvmName("generateCodeForSchema")
public fun DataFrameSchema.generateCode(
markerName: String,
Expand All @@ -77,6 +197,13 @@ public fun DataFrameSchema.generateCode(
).code.declarations.toCodeString()
}

/**
* @include [CommonGenerateCodeDocs]
* {@set [CommonGenerateCodeDocs.RECEIVER] [DataFrameSchema][this]}
* {@set [CommonGenerateCodeDocs.TYPES] interfaces}
*
* @include [Params.MarkerName]
*/
@JvmName("generateInterfacesForSchema")
public fun DataFrameSchema.generateInterfaces(markerName: String): CodeString =
generateCode(
Expand All @@ -85,9 +212,20 @@ public fun DataFrameSchema.generateInterfaces(markerName: String): CodeString =
extensionProperties = false,
)

/**
* @include [CommonGenerateCodeDocs]
* {@set [CommonGenerateCodeDocs.RECEIVER] [DataFrameSchema][this]}
* {@set [CommonGenerateCodeDocs.TYPES] data classes}
*
* @include [Params.MarkerName]
* @include [Params.ExtensionProperties] Default is `false`.
* @include [Params.Visibility] Default is [MarkerVisibility.IMPLICIT_PUBLIC].
* @include [Params.UseFqNames] Default is `false`.
* @include [Params.NameNormalizer] Default is [NameNormalizer.default][NameNormalizer.Companion.default].
*/
@JvmName("generateDataClassesForSchema")
public fun DataFrameSchema.generateDataClasses(
name: String,
markerName: String,
extensionProperties: Boolean = false,
visibility: MarkerVisibility = MarkerVisibility.IMPLICIT_PUBLIC,
useFqNames: Boolean = false,
Expand All @@ -96,7 +234,7 @@ public fun DataFrameSchema.generateDataClasses(
val codeGen = CodeGenerator.create(useFqNames)
return codeGen.generate(
schema = this,
name = name,
name = markerName,
fields = true,
extensionProperties = extensionProperties,
isOpen = false,
Expand All @@ -121,6 +259,10 @@ internal inline fun <reified T> markerName(): String =
*/
public val NameNormalizer.Companion.default: NameNormalizer get() = NameNormalizer.from(setOf('\t', ' ', '_'))

/**
* A value class wrapper for [String], containing
* generated Kotlin code of data schema declarations (markers) and optionally extension properties.
*/
@JvmInline
public value class CodeString(public val value: String) {
override fun toString(): String = value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import org.jetbrains.kotlinx.dataframe.impl.schema.DataFrameSchemaImpl
import org.jetbrains.kotlinx.dataframe.schema.DataFrameSchema
import kotlin.reflect.KClass

/**
* Which type of visibility the marker (generated class-like code declaration)
* will have:
* - [INTERNAL][INTERNAL]: "internal"
* - [IMPLICIT_PUBLIC][IMPLICIT_PUBLIC]: ""
* - [EXPLICIT_PUBLIC][EXPLICIT_PUBLIC]: "public"
*/
public enum class MarkerVisibility {
INTERNAL,
IMPLICIT_PUBLIC,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,7 @@ internal interface DocumentationUrls {

/** [See `explode` on the documentation website.]({@include [Url]}/explode.html) */
interface Explode

/** [See `Data Schemas/Data Classes Generation` on the documentation website.]({@include [Url]}/dataschema-data-classes-generation.html) */
interface DataSchemaGeneration
}
Loading
Loading