commonMain.schemas.ClassSchema.kt Maven / Gradle / Ivy
@file:OptIn(ExperimentalTypeInference::class)
package pt.lightweightform.lfkotlin.schemas
import kotlin.experimental.ExperimentalTypeInference
import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1
import pt.lightweightform.lfkotlin.AllowedValues
import pt.lightweightform.lfkotlin.ComputedValue
import pt.lightweightform.lfkotlin.InitialValue
import pt.lightweightform.lfkotlin.IsRequired
import pt.lightweightform.lfkotlin.Schema
import pt.lightweightform.lfkotlin.Validation
/** Function responsible for creating an instance of type `T` from a map of arguments. */
public typealias ConstructorFunction = (arguments: Map) -> T
/**
* Object holding information about a child of the class schema (class property used to access the
* child + child schema).
*/
public class ChildInfo(public val prop: KMutableProperty1, public val schema: Schema<*>)
/** Interface representing a schema for a class of type [T]. */
public expect interface ClassSchema : Schema {
public var kClass: KClass
public var childInfoByName: Map>
public var constructorFunction: ConstructorFunction?
}
/**
* Builder of a class schema. Use [classSchema] or [nullableClassSchema] to build a class schema.
*/
public class ClassSchemaBuilder {
public val childrenSchemas: MutableMap, Schema<*>> = mutableMapOf()
/** Declare that the child schema of property [property] is [schema]. */
public fun childSchema(property: KMutableProperty1, schema: Schema) {
childrenSchemas[property] = schema
}
/**
* Declare that the child schema of the receiving property is the one returned by
* [schemaBuilder].
*/
public operator fun KMutableProperty1.invoke(schemaBuilder: () -> Schema) {
childSchema(this, schemaBuilder())
}
}
public expect fun classSchemaImpl(
kClass: KClass,
childrenSchemas: Map, Schema<*>>,
constructorFunction: ConstructorFunction?,
isNullable: Boolean,
initialValue: T?,
computedInitialValue: InitialValue?,
computedValue: ComputedValue?,
mismatchedComputedCode: String?,
isClientOnly: Boolean?,
isRequired: Boolean?,
computedIsRequired: IsRequired?,
isRequiredCode: String?,
allowedValues: List?,
computedAllowedValues: AllowedValues?,
disallowedValueCode: String?,
validations: List>?,
initialState: Map?,
extra: Map?
): ClassSchema
/**
* Creates a schema for a class of type [T]. Maps to a schema of type "record" in LF.
*
* Example defining a `PersonSchema` for a class `Person` with `name` and `married` properties:
* ```kotlin
* data class Person(var name: String, var married: Boolean)
*
* val personSchema = classSchema {
* Person::name { stringSchema() }
* Person::married { booleanSchema() }
* }
* ```
*
* When running validations and computed values, LF-Kotlin converts LF "records" (which are simple
* JS objects) into proper instances of the Kotlin class being represented by the schema. By
* default, this conversion occurs by attempting to call a constructor of the class with the value
* of each child as an argument, in the order they were defined in the schema. I.e., in the example
* above, LF-Kotlin will attempt to create a `Person` from a JS object `{name: "Pat", married:
* true}` by calling `Person("Pat", true)`.
*
* If the class' constructor has its parameters in a different order or requires other arguments,
* then the user must provide a [constructorFunction] function, instructing how to properly
* construct the class.
*/
public inline fun classSchema(
noinline constructorFunction: ConstructorFunction? = null,
initialValue: T? = null,
computedInitialValue: InitialValue? = null,
computedValue: ComputedValue? = null,
mismatchedComputedCode: String? = null,
isClientOnly: Boolean? = null,
allowedValues: List? = null,
computedAllowedValues: AllowedValues? = null,
disallowedValueCode: String? = null,
validations: List>? = null,
extra: Map? = null,
initialState: Map? = null,
@BuilderInference classSchemaBuilder: ClassSchemaBuilder.() -> Unit,
): ClassSchema {
val builder = ClassSchemaBuilder()
builder.classSchemaBuilder()
@Suppress("UNCHECKED_CAST")
return classSchemaImpl(
T::class,
builder.childrenSchemas,
constructorFunction,
false,
initialValue,
computedInitialValue,
computedValue as ComputedValue?,
mismatchedComputedCode,
isClientOnly,
null,
null,
null,
allowedValues,
computedAllowedValues,
disallowedValueCode,
validations,
initialState,
extra
) as
ClassSchema
}
/**
* Creates a nullable schema for a class of type [T]. Maps to a schema of type "record" with
* `isNullable` set to `true` in LF.
*
* Example defining a `PersonSchema` for an optional class `Person` with `name` and `married`
* properties:
* ```kotlin
* data class Person(var name: String, var married: Boolean)
*
* val personSchema = nullableClassSchema {
* Person::name { stringSchema() }
* Person::married { booleanSchema() }
* }
* ```
*
* When running validations and computed values, LF-Kotlin converts LF "records" (which are simple
* JS objects) into proper instances of the Kotlin class being represented by the schema. By
* default, this conversion occurs by attempting to call a constructor of the class with the value
* of each child as an argument, in the order they were defined in the schema. I.e., in the example
* above, LF-Kotlin will attempt to create a `Person` from a JS object `{name: "Pat", married:
* true}` by calling `Person("Pat", true)`.
*
* If the class' constructor has its parameters in a different order or requires other arguments,
* then the user must provide a [constructorFunction] function, instructing how to properly
* construct the class.
*/
public inline fun nullableClassSchema(
noinline constructorFunction: ConstructorFunction? = null,
initialValue: T? = null,
computedInitialValue: InitialValue? = null,
computedValue: ComputedValue? = null,
mismatchedComputedCode: String? = null,
isClientOnly: Boolean? = null,
isRequired: Boolean? = null,
computedIsRequired: IsRequired? = null,
isRequiredCode: String? = null,
allowedValues: List? = null,
computedAllowedValues: AllowedValues? = null,
disallowedValueCode: String? = null,
validations: List>? = null,
extra: Map? = null,
initialState: Map? = null,
@BuilderInference classSchemaBuilder: ClassSchemaBuilder.() -> Unit,
): ClassSchema {
val builder = ClassSchemaBuilder()
builder.classSchemaBuilder()
return classSchemaImpl(
T::class,
builder.childrenSchemas,
constructorFunction,
true,
initialValue,
computedInitialValue,
computedValue,
mismatchedComputedCode,
isClientOnly,
isRequired,
computedIsRequired,
isRequiredCode,
allowedValues,
computedAllowedValues,
disallowedValueCode,
validations,
initialState,
extra
)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy