From 9d6205f9ffef8bba13d1e1d13dd6111630b507b0 Mon Sep 17 00:00:00 2001
From: DBotThePony <dbotthepony@yandex.ru>
Date: Fri, 26 Apr 2024 20:19:32 +0700
Subject: [PATCH] Add StreamCodec.Nullable

---
 gradle.properties                             |  2 +-
 .../ru/dbotthepony/kommons/io/StreamCodec.kt  | 54 ++++++++++---------
 2 files changed, 30 insertions(+), 26 deletions(-)

diff --git a/gradle.properties b/gradle.properties
index e04523a..b156a51 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -4,7 +4,7 @@ kotlin.code.style=official
 specifyKotlinAsDependency=false
 
 projectGroup=ru.dbotthepony.kommons
-projectVersion=2.16.0
+projectVersion=2.16.1
 
 guavaDepVersion=33.0.0
 gsonDepVersion=2.8.9
diff --git a/src/main/kotlin/ru/dbotthepony/kommons/io/StreamCodec.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/StreamCodec.kt
index da28fc1..c28e927 100644
--- a/src/main/kotlin/ru/dbotthepony/kommons/io/StreamCodec.kt
+++ b/src/main/kotlin/ru/dbotthepony/kommons/io/StreamCodec.kt
@@ -1,6 +1,7 @@
 package ru.dbotthepony.kommons.io
 
 import ru.dbotthepony.kommons.math.RGBAColor
+import ru.dbotthepony.kommons.util.KOptional
 import java.io.DataInputStream
 import java.io.DataOutputStream
 import java.util.*
@@ -39,29 +40,6 @@ interface StreamCodec<V> {
 			comparator: ((a: V, b: V) -> Boolean) = { a, b -> a == b }
 		) : this({ stream -> reader.invoke(stream) }, writer, copier, comparator)
 
-		val nullable = object : StreamCodec<V?> {
-			override fun read(stream: DataInputStream): V? {
-				return if (stream.read() == 0) null else reader.invoke(stream)
-			}
-
-			override fun write(stream: DataOutputStream, value: V?) {
-				if (value === null) stream.write(0) else {
-					stream.write(1)
-					writer.invoke(stream, value)
-				}
-			}
-
-			override fun copy(value: V?): V? {
-				return if (value === null) null else copier.invoke(value)
-			}
-
-			override fun compare(a: V?, b: V?): Boolean {
-				if (a === null && b === null) return true
-				if (a === null || b === null) return false
-				return comparator.invoke(a, b)
-			}
-		}
-
 		override fun read(stream: DataInputStream): V {
 			return reader.invoke(stream)
 		}
@@ -79,6 +57,31 @@ interface StreamCodec<V> {
 		}
 	}
 
+	class Nullable<V>(val parent: StreamCodec<V>) : StreamCodec<V?> {
+		override fun read(stream: DataInputStream): V? {
+			return if (stream.read() == 0) null else parent.read(stream)
+		}
+
+		override fun write(stream: DataOutputStream, value: V?) {
+			if (value === null)
+				stream.write(0)
+			else {
+				stream.write(1)
+				parent.write(stream, value)
+			}
+		}
+
+		override fun copy(value: V?): V? {
+			return if (value === null) null else parent.copy(value)
+		}
+
+		override fun compare(a: V?, b: V?): Boolean {
+			if (a === null && b === null) return true
+			if (a === null || b === null) return false
+			return parent.compare(a, b)
+		}
+	}
+
 	class Collection<E, C : MutableCollection<E>>(val elementCodec: StreamCodec<E>, val collectionFactory: (Int) -> C) : StreamCodec<C> {
 		override fun read(stream: DataInputStream): C {
 			val size = stream.readVarInt()
@@ -247,8 +250,9 @@ fun <T, R> StreamCodec<T>.map(from: T.() -> R, to: R.() -> T, copy: R.() -> R):
 	return StreamCodec.Mapping(this, from, to, copy)
 }
 
-fun <T : Any> StreamCodec<T>.optional() = StreamCodec.Optional(this)
-fun <T> StreamCodec<T>.koptional() = StreamCodec.KOptional(this)
+fun <T : Any> StreamCodec<T>.optional(): StreamCodec<Optional<T>> = StreamCodec.Optional(this)
+fun <T> StreamCodec<T>.koptional(): StreamCodec<KOptional<T>> = StreamCodec.KOptional(this)
+fun <T> StreamCodec<T>.nullable(): StreamCodec<T?> = StreamCodec.Nullable(this)
 
 val NullValueCodec = StreamCodec.Impl({ _ -> null }, { _, _ -> })
 val BooleanValueCodec = StreamCodec.Impl(DataInputStream::readBoolean, 1L, DataOutputStream::writeBoolean)