Skip to content

Commit 2b19e77

Browse files
committed
Created core stargate API
1 parent 00ec87f commit 2b19e77

File tree

19 files changed

+890
-1
lines changed

19 files changed

+890
-1
lines changed

.idea/gradle.xml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app-shared/app_shared.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@ Pod::Spec.new do |spec|
5353
spec.resources = ['build/compose/cocoapods/compose-resources']
5454
spec.info_plist = {
5555
"CFBundleShortVersionString": "1.0.0-beta10",
56-
"CFBundleVersion": "712"
56+
"CFBundleVersion": "714"
5757
}
5858
end

component-stargate/build.gradle.kts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ kotlin {
3838
// Logging
3939
// https://github.com/mooncloak/logpile
4040
implementation("com.mooncloak.kodetools.logpile:logpile-core:_")
41+
42+
// Multiplatform I/O - kotlinx-io
43+
// https://github.com/Kotlin/kotlinx-io
44+
// Apache 2.0: https://github.com/Kotlin/kotlinx-io/blob/master/LICENSE
45+
implementation("org.jetbrains.kotlinx:kotlinx-io-core:_")
46+
47+
// UI Components - Needed for content models
48+
implementation(compose.runtime)
49+
implementation(compose.ui)
50+
51+
// Jetpack Compose Serializers - compose-serialization
52+
// https://github.com/mooncloak/compose-serialization
53+
// Apache 2.0: https://github.com/mooncloak/compose-serialization
54+
implementation("com.mooncloak.kodetools.compose.serialization:compose-serialization-core:_")
4155
}
4256
}
4357

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.mooncloak.vpn.component.stargate
2+
3+
public interface ConnectionPoint {
4+
5+
public val name: String?
6+
7+
public companion object
8+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.mooncloak.vpn.component.stargate
2+
3+
import kotlinx.io.Sink
4+
import kotlinx.io.Source
5+
6+
public interface ContentWrapper {
7+
8+
public suspend fun wrap(sink: Sink, block: suspend Sink.() -> Unit)
9+
10+
public suspend fun unwrap(source: Source, block: suspend Source.() -> Unit)
11+
12+
public companion object
13+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.mooncloak.vpn.component.stargate
2+
3+
import kotlinx.datetime.Instant
4+
import kotlinx.serialization.KSerializer
5+
import kotlinx.serialization.builtins.serializer
6+
import kotlinx.serialization.descriptors.SerialDescriptor
7+
import kotlinx.serialization.encoding.Decoder
8+
import kotlinx.serialization.encoding.Encoder
9+
10+
public object SecondsSinceEpochSerializer : KSerializer<Instant> {
11+
12+
override val descriptor: SerialDescriptor = Long.serializer().descriptor
13+
14+
15+
override fun serialize(encoder: Encoder, value: Instant) {
16+
encoder.encodeLong(value.epochSeconds)
17+
}
18+
19+
override fun deserialize(decoder: Decoder): Instant =
20+
Instant.fromEpochSeconds(decoder.decodeLong())
21+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package com.mooncloak.vpn.component.stargate
2+
3+
import kotlinx.coroutines.CoroutineDispatcher
4+
import kotlinx.coroutines.flow.Flow
5+
import kotlinx.coroutines.flow.MutableStateFlow
6+
import kotlinx.coroutines.flow.filterNotNull
7+
import kotlinx.coroutines.flow.map
8+
import kotlinx.coroutines.sync.Mutex
9+
import kotlinx.coroutines.sync.withLock
10+
import kotlinx.coroutines.withContext
11+
import kotlinx.io.IOException
12+
import kotlinx.io.Sink
13+
import kotlinx.io.Source
14+
import kotlinx.serialization.KSerializer
15+
import kotlinx.serialization.SerialFormat
16+
import kotlin.coroutines.cancellation.CancellationException
17+
import kotlin.reflect.KClass
18+
19+
public interface Tunnel : AutoCloseable {
20+
21+
public val isConnected: Boolean
22+
23+
public val connectionPoints: List<ConnectionPoint>
24+
25+
public override fun close()
26+
27+
public companion object
28+
}
29+
30+
public interface RawTunnel : Tunnel {
31+
32+
@Throws(IllegalStateException::class, IOException::class, CancellationException::class)
33+
public suspend fun source(): Source
34+
35+
@Throws(IllegalStateException::class, IOException::class, CancellationException::class)
36+
public suspend fun sink(): Sink
37+
38+
@Throws(IllegalStateException::class, IOException::class, CancellationException::class)
39+
public suspend fun send(block: suspend Sink.() -> Unit) {
40+
val sink = sink()
41+
42+
block.invoke(sink)
43+
}
44+
45+
@Throws(IllegalStateException::class, IOException::class, CancellationException::class)
46+
public suspend fun receive(block: suspend Source.() -> Unit) {
47+
val source = source()
48+
49+
block.invoke(source)
50+
}
51+
52+
public companion object
53+
}
54+
55+
public interface SerialTunnel<T : Any> : Tunnel {
56+
57+
public val format: SerialFormat
58+
59+
public val serializer: KSerializer<T>
60+
61+
public val kClass: KClass<T>
62+
63+
@Throws(IllegalStateException::class, IOException::class, CancellationException::class)
64+
public suspend fun send(value: T?)
65+
66+
@Throws(IllegalStateException::class, IOException::class, CancellationException::class)
67+
public suspend fun receive(): T?
68+
69+
public fun receiveAll(): Flow<T?>
70+
71+
public companion object
72+
}
73+
74+
public abstract class BaseSerialTunnel<T : Any> public constructor(
75+
private val dispatcher: CoroutineDispatcher
76+
) : SerialTunnel<T> {
77+
78+
protected abstract val contentWrapper: ContentWrapper
79+
80+
protected abstract suspend fun source(): Source
81+
82+
protected abstract suspend fun sink(): Sink
83+
84+
protected abstract suspend fun Sink.encode(value: T?)
85+
86+
protected abstract suspend fun Source.decode(): T?
87+
88+
private val mutex = Mutex(locked = false)
89+
90+
private val stateFlow = MutableStateFlow<ReceivedContent<T>?>(null)
91+
92+
final override suspend fun send(value: T?) {
93+
withContext(dispatcher) {
94+
mutex.withLock {
95+
if (!isConnected) {
96+
error("Cannot send data. Tunnel is not connected.")
97+
}
98+
99+
val sink = sink()
100+
101+
contentWrapper.wrap(sink) {
102+
this.encode(value)
103+
}
104+
}
105+
}
106+
}
107+
108+
final override suspend fun receive(): T? =
109+
withContext(dispatcher) {
110+
mutex.withLock {
111+
if (!isConnected){
112+
error("Cannot receive data. Tunnel is not connected.")
113+
}
114+
115+
var value: T? = null
116+
117+
val source = source()
118+
119+
contentWrapper.unwrap(source) {
120+
value = this.decode()
121+
}
122+
123+
stateFlow.emit(ReceivedContent(value = value))
124+
125+
return@withContext value
126+
}
127+
}
128+
129+
final override fun receiveAll(): Flow<T?> =
130+
stateFlow.filterNotNull()
131+
.map { content -> content.value }
132+
133+
override fun close() {
134+
}
135+
}
136+
137+
private data class ReceivedContent<T : Any>(
138+
val value: T?
139+
)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.mooncloak.vpn.component.stargate.message.model
2+
3+
import kotlinx.serialization.Serializable
4+
import kotlin.jvm.JvmInline
5+
6+
/**
7+
* Represents a compacted image placeholder.
8+
*
9+
* @property [value] The actual blur hash value.
10+
*
11+
* @see [blurha.sh Website](https://blurha.sh/)
12+
* @see [blurhash Repository](https://github.com/woltapp/blurhash)
13+
*/
14+
@JvmInline
15+
@Serializable
16+
public value class BlurHash public constructor(
17+
public val value: String
18+
)

0 commit comments

Comments
 (0)