All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.Schemas.kt Maven / Gradle / Ivy

There is a newer version: 0.23.0
Show newest version
package io.kform

import kotlin.js.JsName
import kotlin.reflect.KType
import kotlinx.coroutines.flow.Flow

/** Bus used by schemas to emit events representing "what happened" to values. */
public interface SchemaEventsBus {
    /** Emit the event representing "what happened" with the value. */
    public suspend fun emit(event: ValueEvent<*>)
}

/**
 * Schema representing metadata on values of type [T].
 *
 * The schema of a form is responsible for specifying which validations to run on it, as well as how
 * to "manage" the form data. If a certain form contains a field of, for example, type [Int], then
 * there should be a schema of type `Schema` that holds the metadata required for knowing how
 * to, for example, initialise said field and validate it.
 *
 * The [form manager][FormManager] uses schemas extensively to, amongst others, initialise data, set
 * data, and validate data; the schema is responsible for specifying **how** each different type of
 * data is initialised or set.
 */
@JsName("SchemaKt")
public interface Schema {
    /** Information about the type of value represented by this schema. */
    public val typeInfo: TypeInfo

    /** List of validations used to validate this schema. */
    public val validations: List>

    /**
     * Initial value for a value of this schema.
     *
     * New values of this schema will hold this value by default. Moreover, when a value of this
     * schema is [reset][FormManager.reset] by the [form manager][FormManager], it is set to this
     * value.
     */
    public val initialValue: T

    /** Returns a clone (deep copy) of [value]. */
    public suspend fun clone(value: T): T

    /**
     * Whether a value of this schema can be assigned to a variable with the provided [type].
     *
     * This is used by [validations][Validation] to check, at runtime, whether dependency values can
     * be assigned to variables of a certain [type].
     *
     * Unless overriden, this function simply returns `true`, meaning that values can be assigned to
     * any type.
     */
    public fun assignableTo(type: KType): Boolean = true

    /**
     * Initialises and returns a value of this schema from a given value [fromValue] to be stored by
     * the [form manager][FormManager], sending events about the initialisation of the value through
     * [eventsBus]. The provided [path] represents the path of the value being initialised within
     * the [form manager][FormManager].
     *
     * Initialisation events should be emitted "bottom up": this means that an event signaling the
     * initialisation of a parent value must only be emitted **after** all events signaling the
     * initialisation of its children. As a side effect of this, implementations of [init] may bail
     * when their coroutine context is no longer active (meaning that the coroutine was cancelled
     * due to another operation that "overrides" this operation) and perform less work than
     * necessary (e.g. when initialising a list, we may stop initialising more elements).
     */
    public suspend fun init(path: AbsolutePath, fromValue: Any?, eventsBus: SchemaEventsBus): T

    /**
     * Changes [value], a value of this schema stored by the [form manager][FormManager] into the
     * given value [intoValue], sending events about the change through [eventsBus]. The provided
     * [path] represents the path of the value being changed within the [form manager][FormManager].
     * The new value is returned; for parent schemas, it should typically be the same instance of
     * [value], but mutated to resemble [intoValue].
     *
     * Because the returned value is the "real" value stored by the [form manager][FormManager],
     * emitted events should **never** reference it or any of its children, as doing so would bypass
     * the [form manager][FormManager]'s protections against concurrent access to its data. Events
     * should instead reference parts of [intoValue], and the returned value should resemble it.
     */
    public suspend fun change(
        path: AbsolutePath,
        value: T,
        intoValue: Any?,
        eventsBus: SchemaEventsBus
    ): T

    /**
     * Destroys and returns [value], a value of this schema stored by the [form manager]
     * [FormManager], sending events about the destruction through [eventsBus]. The provided [path]
     * represents the path of the value being destroyed within the [form manager][FormManager].
     *
     * Destruction events should be emitted "top down": this means that an event signaling the
     * destruction of a parent value must be emitted **before** all events signaling the destruction
     * of its children. As a side effect of this, implementations of [destroy] **must not** bail
     * when their coroutine context is no longer active, since the early signaling of the parent
     * value being destroyed means that all children must also have their destruction signaled.
     *
     * Because the value is being removed from the [form manager][FormManager], emitted events can
     * (and should) reference parts of [value].
     */
    public suspend fun destroy(path: AbsolutePath, value: T, eventsBus: SchemaEventsBus): T
}

/**
 * Schema representing metadata on parent values of type [T].
 *
 * "Parent" values, in this context, simply means values with children (children themselves also
 * represented by their own schemas).
 */
@JsName("ParentSchemaKt")
public interface ParentSchema : Schema {
    /** Whether this schema supports setting values concurrently. */
    public val supportsConcurrentSets: Boolean
        get() = false

    /**
     * Returns whether [fragment] is a valid identifier for a child schema of this schema.
     *
     * A wildcard fragment is always assumed valid (representing all valid children schemas) and the
     * [form manager][FormManager] will never call this function with one. Similarly, a recursive
     * wildcard fragment will never be passed to this function.
     */
    public fun isValidChildSchemaFragment(fragment: AbsolutePathFragment): Boolean

    /**
     * Returns a sequence of information on the schemas matching [fragment] that are children of
     * this schema. The provided [path] and [queried path][queriedPath] respectively represent the
     * path of this schema and the path of the queried value within the [form manager][FormManager].
     *
     * The provided fragment will either be a wildcard or a valid fragment according to
     * [isValidChildSchemaFragment]. If the function is called with a wildcard fragment, then all
     * children schemas should be yielded. A recursive wildcard fragment will never be passed to
     * this function by the form manager.
     */
    public fun childrenSchemas(
        path: AbsolutePath,
        queriedPath: AbsolutePath,
        fragment: AbsolutePathFragment
    ): Sequence>

    /**
     * Returns whether [fragment] is a valid identifier for a child of [value]: a value of this
     * schema.
     *
     * A wildcard fragment is always assumed valid (representing all valid children) and the
     * [form manager][FormManager] will never call this function with one. Similarly, a recursive
     * wildcard fragment will never be passed to this function.
     */
    public suspend fun isValidChildFragment(value: T, fragment: AbsolutePathFragment): Boolean

    /**
     * Returns a flow of information on the values matching [fragment] that are children of [value],
     * a value of this schema.
     *
     * The provided fragment will either be a wildcard or a valid fragment according to
     * [isValidChildFragment]. If the function is called with a wildcard fragment, then all children
     * should be emitted. A recursive wildcard fragment will never be passed to this function by the
     * [form manager][FormManager].
     */
    public fun children(
        path: AbsolutePath,
        schemaPath: AbsolutePath,
        value: T,
        fragment: AbsolutePathFragment
    ): Flow>

    /**
     * Returns whether [fragment] can be used to set a child of [value], a value of this schema.
     *
     * Wildcard fragments (recursive or otherwise) will never be passed to this method by the
     * [form manager][FormManager].
     */
    public suspend fun isValidSetFragment(value: T, fragment: AbsolutePathFragment): Boolean

    /**
     * Sets the child(ren) of [value], a value of this schema, identified by [fragment] with
     * [childValue], sending events about all modifications through [eventsBus]. The provided [path]
     * represents the path of [value] within the [form manager][FormManager].
     *
     * The provided fragment is either a wildcard fragment or a valid fragment according to
     * [isValidSetFragment]. A recursive wildcard fragment will never be passed to this function by
     * the [form manager][FormManager]. If a simple wildcard fragment is passed, then all children
     * should be set to [childValue].
     */
    public suspend fun set(
        path: AbsolutePath,
        value: T,
        fragment: AbsolutePathFragment,
        childValue: Any?,
        eventsBus: SchemaEventsBus
    )

    /** Returns a "container" used to hold the states of the children of a value of this schema. */
    public fun childrenStatesContainer(): ParentState
}

/**
 * Schema representing metadata on collections of type [T] with children of type [TChildren].
 *
 * "Collections", in this context, simply means data structures that hold children of the same type
 * and that support the addition and removal of values. Collection schemas may represent structures
 * other than [Kotlin collections][Collection], e.g. a `MapSchema` can be created that represents
 * [maps][Map] (even though they don't extend [Collection]) since they allow addition and removal of
 * values and all values must be of the same type.
 */
@JsName("CollectionSchemaKt")
public interface CollectionSchema : ParentSchema {
    /** Whether this schema supports removing values concurrently. */
    public val supportsConcurrentRemoves: Boolean
        get() = false

    public override fun childrenSchemas(
        path: AbsolutePath,
        queriedPath: AbsolutePath,
        fragment: AbsolutePathFragment
    ): Sequence>

    public override fun children(
        path: AbsolutePath,
        schemaPath: AbsolutePath,
        value: T,
        fragment: AbsolutePathFragment
    ): Flow>

    /**
     * Returns whether [fragment] can be used to remove a child of [value], a value of this schema.
     *
     * Wildcard fragments (recursive or otherwise) will never be passed to this method by the
     * [form manager][FormManager].
     */
    public suspend fun isValidRemoveFragment(value: T, fragment: AbsolutePathFragment): Boolean

    /**
     * Removes from [value], a value of this schema, all children identified by [fragment], sending
     * events about all modifications through [eventsBus]. The provided [path] represents the path
     * of [value] within the [form manager][FormManager].
     *
     * The provided fragment is either a wildcard fragment or a valid fragment according to
     * [isValidRemoveFragment]. A recursive wildcard fragment will never be passed to this function
     * by the [form manager][FormManager]. If a simple wildcard fragment is passed, then all
     * children of [value] should be removed.
     */
    public suspend fun remove(
        path: AbsolutePath,
        value: T,
        fragment: AbsolutePathFragment,
        eventsBus: SchemaEventsBus
    )

    public override fun childrenStatesContainer(): CollectionState
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy