Skip to content
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
Expand Up @@ -4,6 +4,37 @@ import com.redmadrobot.konfeature.source.SourceSelectionStrategy
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

/**
* Abstract base class for defining feature configurations.
*
* FeatureConfig provides a declarative way to define configuration schemas using
* property delegates. Each configuration element is defined using either `by toggle()`
* for Boolean values or `by value()` for other types.
*
* Example usage:
* ```kotlin
* class MyFeatureConfig : FeatureConfig(
* name = "my_feature",
* description = "Configuration for my feature"
* ) {
* val isEnabled: Boolean by toggle(
* key = "my_feature_enabled",
* description = "Enable/disable my feature",
* defaultValue = false,
* sourceSelectionStrategy = SourceSelectionStrategy.Any
* )
*
* val timeout: Long by value(
* key = "my_feature_timeout",
* description = "Timeout in milliseconds",
* defaultValue = 5000L
* )
* }
* ```
*
* @param name unique identifier for this configuration
* @param description human-readable description of this configuration's purpose
*/
public abstract class FeatureConfig(
override val name: String,
override val description: String
Expand Down Expand Up @@ -34,6 +65,16 @@ public abstract class FeatureConfig(
error("Use toggle instead of boolean value")
}

/**
* Creates a configuration value delegate for non-Boolean types.
*
* @param T the type of the configuration value
* @param key unique identifier for this value used in sources
* @param description human-readable description of this value's purpose
* @param defaultValue fallback value when no sources provide a value
* @param sourceSelectionStrategy strategy for selecting which sources can provide values
* @return a property delegate that resolves the configuration value
*/
public fun <T : Any> value(
key: String,
description: String,
Expand All @@ -48,6 +89,18 @@ public abstract class FeatureConfig(
)
}

/**
* Creates a configuration toggle delegate for Boolean values.
*
* This method should be used instead of `value()` for Boolean configuration
* elements to provide better semantic clarity for feature flags and toggles.
*
* @param key unique identifier for this toggle used in sources
* @param description human-readable description of this toggle's purpose
* @param defaultValue fallback value when no sources provide a value
* @param sourceSelectionStrategy strategy for selecting which sources can provide values
* @return a property delegate that resolves the Boolean configuration value
*/
public fun toggle(
key: String,
description: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,36 @@
package com.redmadrobot.konfeature

/**
* Interface representing the specification of a feature configuration.
*
* This interface provides metadata about a feature configuration including
* its name, description, and the list of configuration values it contains.
* It's implemented by [FeatureConfig] and used for introspection and
* building debug interfaces.
*
* @see FeatureConfig for the concrete implementation
*/
public interface FeatureConfigSpec {
/**
* Unique name identifying this feature configuration.
*
* The name should be descriptive and unique among all registered configurations.
*/
public val name: String

/**
* Human-readable description of what this feature configuration controls.
*
* This description helps developers understand the purpose and scope of
* the configuration when viewing it in debug panels or documentation.
*/
public val description: String

/**
* List of all configuration value specifications contained in this configuration.
*
* This provides access to metadata about each individual configuration element,
* including their keys, descriptions, default values, and source selection strategies.
*/
public val values: List<FeatureValueSpec<out Any>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ package com.redmadrobot.konfeature
import com.redmadrobot.konfeature.source.FeatureValueSource
import dev.drewhamilton.poko.Poko

/**
* Represents a resolved configuration value along with information about its source.
*
* This class encapsulates both the actual configuration value and metadata about
* where the value came from (default, source, or interceptor). This information
* is useful for debugging, logging, and understanding the configuration resolution flow.
*
* @param T the type of the configuration value
* @property source information about where this value originated from
* @property value the resolved configuration value
*/
@Poko
public class FeatureValue<T>(
public val source: FeatureValueSource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@ package com.redmadrobot.konfeature
import com.redmadrobot.konfeature.source.SourceSelectionStrategy
import dev.drewhamilton.poko.Poko

/**
* Specification for a feature configuration value element.
*
* This class defines the metadata and behavior for a single configuration element
* within a [FeatureConfig]. It includes the key used to look up values in sources,
* documentation, fallback behavior, and source selection rules.
*
* @param T the type of the configuration value
* @property key the unique identifier used to retrieve this value from sources
* @property description human-readable description of what this configuration element controls
* @property defaultValue the fallback value used when no sources provide a value
* @property sourceSelectionStrategy strategy for determining which sources can provide values
*/
@Poko
public class FeatureValueSpec<T : Any>(
public val key: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
package com.redmadrobot.konfeature

/**
* Main entry point for the Konfeature library that manages feature configuration.
*
* Konfeature provides a unified way to work with remote feature configurations by combining
* multiple data sources, interceptors, and providing runtime access to configuration values.
*
* @see FeatureConfig for defining configuration schemas
* @see com.redmadrobot.konfeature.source.FeatureSource for implementing configuration data sources
* @see com.redmadrobot.konfeature.source.Interceptor for implementing value overrides
*/
public interface Konfeature {

/**
* List of all registered feature configuration specifications.
*
* This property provides access to metadata about all registered [FeatureConfig] instances,
* including their names, descriptions, and value specifications. Useful for building
* debug panels or configuration management UIs.
*/
public val spec: List<FeatureConfigSpec>

/**
* Retrieves the current value for a given feature configuration specification.
*
* The value resolution follows this order:
* 1. Default value is assigned
* 2. Sources are filtered using the spec's [com.redmadrobot.konfeature.source.SourceSelectionStrategy]
* 3. Sources are searched in registration order (first match wins)
* 4. Interceptors are applied in registration order (last non-null wins)
*
* @param T the type of the configuration value
* @param spec the feature value specification defining the configuration element
* @return [FeatureValue] containing the resolved value and its source
*/
public fun <T : Any> getValue(spec: FeatureValueSpec<T>): FeatureValue<T>
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,40 @@
package com.redmadrobot.konfeature

/**
* Interface for logging Konfeature events and errors.
*
* The logger is used to track configuration value access, type mismatches,
* and other diagnostic information. Implementations can integrate with
* existing logging frameworks like Timber, SLF4J, or custom solutions.
*
* Events that are logged include:
* - Configuration value access with source information
* - Type mismatch warnings when sources return unexpected types
* - Configuration validation warnings
*/
public interface Logger {

/**
* Logs a message with the specified severity level.
*
* @param severity the severity level of the log message
* @param message the message to log
*/
public fun log(severity: Severity, message: String)

/**
* Severity levels for log messages.
*/
public enum class Severity {
WARNING, INFO
/**
* Warning level for non-critical issues like type mismatches or empty configurations.
*/
WARNING,

/**
* Information level for normal operations like value access and source information.
*/
INFO
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,49 @@ import com.redmadrobot.konfeature.exception.SourceNameAlreadyExistException
import com.redmadrobot.konfeature.source.FeatureSource
import com.redmadrobot.konfeature.source.Interceptor

/**
* Builder class for configuring and creating a [Konfeature] instance.
*
* KonfeatureBuilder uses the builder pattern to configure all aspects of a Konfeature
* instance including sources, interceptors, feature configurations, and logging.
* It validates the configuration during the build process to ensure consistency.
*
* Example usage:
* ```kotlin
* val konfeature = konfeature {
* addSource(FirebaseFeatureSource(remoteConfig))
* addInterceptor(DebugPanelInterceptor())
* register(MyFeatureConfig())
* setLogger(TimberLogger())
* }
* ```
*
* @see konfeature for the DSL function that creates and configures a builder
*/
public class KonfeatureBuilder {
private val sources = mutableListOf<FeatureSource>()
private var interceptors = mutableListOf<Interceptor>()
private var spec = mutableListOf<FeatureConfig>()
private var logger: Logger? = null

/**
* Adds an interceptor to the Konfeature configuration.
*
* @param interceptor the interceptor to add
* @return this builder instance for method chaining
*/
public fun addInterceptor(interceptor: Interceptor): KonfeatureBuilder {
interceptors.add(interceptor)
return this
}

/**
* Adds a feature source to the Konfeature configuration.
*
* @param source the source to add
* @return this builder instance for method chaining
* @throws SourceNameAlreadyExistException if a source with the same name already exists
*/
public fun addSource(source: FeatureSource): KonfeatureBuilder {
if (sources.any { it.name == source.name }) {
throw SourceNameAlreadyExistException(source.name)
Expand All @@ -28,6 +60,13 @@ public class KonfeatureBuilder {
return this
}

/**
* Registers a feature configuration with the Konfeature instance.
*
* @param featureConfig the configuration to register
* @return this builder instance for method chaining
* @throws ConfigNameAlreadyExistException if a config with the same name already exists
*/
public fun register(featureConfig: FeatureConfig): KonfeatureBuilder {
if (spec.any { it.name == featureConfig.name }) {
throw ConfigNameAlreadyExistException(featureConfig.name)
Expand All @@ -36,11 +75,28 @@ public class KonfeatureBuilder {
return this
}

/**
* Sets the logger for the Konfeature instance.
*
* @param logger the logger to use for Konfeature events
* @return this builder instance for method chaining
*/
public fun setLogger(logger: Logger): KonfeatureBuilder {
this.logger = logger
return this
}

/**
* Builds and returns a configured Konfeature instance.
*
* This method validates the configuration and throws exceptions if:
* - No feature configurations are registered
* - Feature configurations have duplicate keys
*
* @return a fully configured Konfeature instance
* @throws NoFeatureConfigException if no configurations are registered
* @throws KeyDuplicationException if configurations have duplicate keys
*/
public fun build(): Konfeature {
if (spec.isEmpty()) throw NoFeatureConfigException()

Expand Down Expand Up @@ -81,6 +137,16 @@ public class KonfeatureBuilder {
}
}

/**
* DSL function for creating and configuring a Konfeature instance.
*
* This function provides a convenient way to configure a Konfeature instance
* using a builder DSL. It creates a KonfeatureBuilder, applies the configuration
* block, and returns the built Konfeature instance.
*
* @param build configuration block applied to the KonfeatureBuilder
* @return a configured Konfeature instance
*/
public fun konfeature(build: KonfeatureBuilder.() -> Unit): Konfeature {
return KonfeatureBuilder().apply(build).build()
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
package com.redmadrobot.konfeature.exception

/**
* Base sealed class for all Konfeature-specific exceptions.
*
* All exceptions thrown by the Konfeature library inherit from this class,
* allowing for comprehensive error handling when working with the library.
*/
public sealed class KonfeatureException(messageProvider: () -> String) : Exception(messageProvider.invoke())

/**
* Exception thrown when attempting to register a feature configuration with a name that already exists.
*
* @param name the duplicate configuration name
*/
public class ConfigNameAlreadyExistException(
name: String
) : KonfeatureException({ "feature config with name '$name' already registered" })

/**
* Exception thrown when a feature configuration contains duplicate keys.
*
* @param values the list of duplicate keys
* @param config the name of the configuration containing duplicates
*/
public class KeyDuplicationException(
values: List<String>,
config: String
Expand All @@ -14,8 +31,16 @@ public class KeyDuplicationException(
"values with keys <$duplicatedValues> are duplicated in config '$config'"
})

/**
* Exception thrown when attempting to build a Konfeature instance without any registered configurations.
*/
public class NoFeatureConfigException : KonfeatureException({ "No feature config added" })

/**
* Exception thrown when attempting to register a source with a name that already exists.
*
* @param name the duplicate source name
*/
public class SourceNameAlreadyExistException(
name: String
) : KonfeatureException({ "source with name '$name' already registered" })
Loading
Loading