diff --git a/CHANGELOG.md b/CHANGELOG.md index f1dc21eb..16af39ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [Unreleased] ### Added +- Add `specOrNull` delegate to meta and Scheme ### Changed - `Factory` is now `fun interface` and uses `build` instead of `invoke`. `invoke moved to an extension. diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Specification.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Specification.kt index d92b4a3b..4cac7c72 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Specification.kt @@ -43,7 +43,7 @@ public interface Specification : ReadOnlySpecification { */ public fun MutableMeta.updateWith( spec: Specification, - action: T.() -> Unit + action: T.() -> Unit, ): T = spec.write(this).apply(action) @@ -82,6 +82,31 @@ public fun Scheme.spec( key: Name? = null, ): ReadWriteProperty = meta.spec(spec, key) +/** + * A delegate that uses a [Specification] to wrap a child of this provider. + * Returns null if meta with given name does not exist. + */ +public fun MutableMeta.specOrNull( + spec: Specification, + key: Name? = null, +): ReadWriteProperty = object : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): T? { + val name = key ?: property.name.asName() + return if (get(name) == null) null else spec.write(getOrCreate(name)) + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { + val name = key ?: property.name.asName() + if (value == null) remove(name) + else set(name, value.toMeta()) + } +} + +public fun Scheme.specOrNull( + spec: Specification, + key: Name? = null, +): ReadWriteProperty = meta.specOrNull(spec, key) + /** * A delegate that uses a [Specification] to wrap a list of child providers. * If children are mutable, the changes in list elements are reflected on them.