Skip to content

Commit 2b1256e

Browse files
committed
wip deserialization
Signed-off-by: Adam Ratzman <[email protected]>
1 parent e3009ec commit 2b1256e

File tree

41 files changed

+495
-212
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+495
-212
lines changed

build.gradle.kts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,17 +146,30 @@ kotlin {
146146
}
147147
}
148148

149+
val hostOs = System.getProperty("os.name")
150+
val isMingwX64 = hostOs.startsWith("Windows")
151+
val nativeTarget = when {
152+
hostOs == "Mac OS X" -> macosX64("native")
153+
hostOs == "Linux" -> linuxX64("native")
154+
isMingwX64 -> mingwX64("native")
155+
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
156+
}
157+
158+
149159
targets {
150160
sourceSets {
151-
val coroutineVersion = "1.4.2"
161+
val coroutineVersion = "1.4.2-native-mt"
152162
val serializationVersion = "1.0.1"
153163
val ktorVersion = "1.4.1"
164+
val kotlinxDatetimeVersion = "0.1.1"
154165

155166
val commonMain by getting {
156167
dependencies {
157168
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion")
158169
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
159170
implementation("io.ktor:ktor-client-core:$ktorVersion")
171+
implementation("org.jetbrains.kotlinx:kotlinx-datetime:$kotlinxDatetimeVersion")
172+
implementation("com.autodesk:coroutineworker:0.6.2")
160173
}
161174
}
162175
val commonTest by getting {
@@ -221,6 +234,17 @@ kotlin {
221234
}
222235
}
223236

237+
val nativeMain by getting {
238+
dependencies {
239+
implementation("io.ktor:ktor-client-curl:$ktorVersion")
240+
}
241+
}
242+
val nativeTest by getting {
243+
dependencies {
244+
245+
}
246+
}
247+
224248
all {
225249
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
226250
}
@@ -260,8 +284,9 @@ publishing {
260284

261285
signing {
262286
if (project.hasProperty("signing.keyId")
263-
&& project.hasProperty("signing.password")
264-
&& project.hasProperty("signing.secretKeyRingFile")) {
287+
&& project.hasProperty("signing.password")
288+
&& project.hasProperty("signing.secretKeyRingFile")
289+
) {
265290
sign(publishing.publications)
266291
}
267292
}

src/androidMain/kotlin/com/adamratzman/spotify/http/Endpoints.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import android.os.Build.VERSION_CODES
66
import android.util.Base64
77
import java.net.URLEncoder
88

9+
actual fun SpotifyCache.f() {}
10+
911
internal actual fun String.encodeUrl() = URLEncoder.encode(this, "UTF-8")!!
1012

1113
internal actual fun String.base64ByteEncode(): String {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
/* Spotify Web API, Kotlin Wrapper; MIT License, 2017-2021; Original author: Adam Ratzman */
22
package com.adamratzman.spotify.utils
33

4-
public actual typealias TimeUnit = java.util.concurrent.TimeUnit
4+
public actual typealias TimeUnit = java.util.concurrent.TimeUnit
Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
11
/* Spotify Web API, Kotlin Wrapper; MIT License, 2017-2021; Original author: Adam Ratzman */
22
package com.adamratzman.spotify.utils
33

4-
import android.annotation.SuppressLint
5-
import java.text.SimpleDateFormat
6-
import java.util.Date
7-
84
public actual fun getCurrentTimeMs(): Long = System.currentTimeMillis()
9-
10-
@SuppressLint("SimpleDateFormat")
11-
internal actual fun formatDate(format: String, date: Long): String {
12-
return SimpleDateFormat(format).format(Date(date))
13-
}

src/commonMain/kotlin/com.adamratzman.spotify/endpoints/public/BrowseApi.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,7 @@ public class BrowseApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
101101
endpointBuilder("/browse/featured-playlists").with("limit", limit).with("offset", offset).with(
102102
"market",
103103
market?.name
104-
).with("locale", locale).with("timestamp", timestamp?.let {
105-
formatDate("yyyy-MM-dd'T'HH:mm:ss", it)
106-
}).toString()
104+
).with("locale", locale).with("timestamp", timestamp?.let { formatDate(it) }).toString()
107105
).toObject(FeaturedPlaylists.serializer(), api, json)
108106

109107
/**
@@ -182,8 +180,8 @@ public class BrowseApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
182180
"limit",
183181
limit
184182
).with("offset", offset)
185-
.with("market", market?.name).toString()
186-
).toPagingObject(SimplePlaylist.serializer(), "playlists", endpoint = this, json = json)
183+
.with("market", market?.name).toString().apply { println("here1") }
184+
).toPagingObject(SimplePlaylist.serializer(), "playlists", endpoint = this, json = json).apply { println("heref") }
187185

188186
/**
189187
* Create a playlist-style listening experience based on seed artists, tracks and genres.

src/commonMain/kotlin/com.adamratzman.spotify/http/Endpoints.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,16 @@ import kotlinx.coroutines.withTimeout
1616
import kotlinx.serialization.Serializable
1717
import kotlinx.serialization.Transient
1818

19+
expect fun SpotifyCache.f()
20+
1921
public abstract class SpotifyEndpoint(public val api: GenericSpotifyApi) {
2022
public val cache: SpotifyCache = SpotifyCache()
2123
internal val json get() = api.spotifyApiOptions.json
2224

25+
init {
26+
cache.f()
27+
}
28+
2329
internal fun endpointBuilder(path: String) = EndpointBuilder(path, api)
2430

2531
protected fun checkBulkRequesting(maxSize: Int, itemSize: Int) {

src/commonMain/kotlin/com.adamratzman.spotify/models/serialization/SerializationUtils.kt

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import kotlin.reflect.KClass
1414
import kotlinx.serialization.KSerializer
1515
import kotlinx.serialization.builtins.MapSerializer
1616
import kotlinx.serialization.builtins.serializer
17+
import kotlinx.serialization.decodeFromString
18+
import kotlinx.serialization.encodeToString
1719
import kotlinx.serialization.json.Json
1820
import kotlinx.serialization.json.JsonElement
1921
import kotlinx.serialization.json.JsonObject
@@ -57,7 +59,7 @@ internal inline fun <reified T> String.toList(serializer: KSerializer<List<T>>,
5759
}
5860
}
5961

60-
internal fun <T : Any> String.toPagingObject(
62+
internal inline fun <reified T : Any> String.toPagingObject(
6163
tClazz: KClass<T>,
6264
tSerializer: KSerializer<T>,
6365
innerObjectName: String? = null,
@@ -66,11 +68,21 @@ internal fun <T : Any> String.toPagingObject(
6668
arbitraryInnerNameAllowed: Boolean = false,
6769
skipInnerNameFirstIfPossible: Boolean = true
6870
): NullablePagingObject<T> {
71+
println("here2")
6972
if (innerObjectName != null || (arbitraryInnerNameAllowed && !skipInnerNameFirstIfPossible)) {
73+
println("here3 $tClazz")
7074
val map = this.parseJson {
71-
val t = (String.serializer() to NullablePagingObject.serializer(tSerializer))
72-
json.decodeFromString(MapSerializer(t.first, t.second), this)
75+
val t = (String.serializer() to NullablePagingObject.serializer(tSerializer)).apply { println("serializer done") }
76+
val jsonObjectRoot = (json.parseToJsonElement(this) as JsonObject)
77+
val jsonElement = innerObjectName?.let { jsonObjectRoot[it] } ?: jsonObjectRoot.keys.firstOrNull()?.let { jsonObjectRoot[it] }
78+
?: throw SpotifyException.ParseException("Json element was null for class $tClazz (json $this)")
79+
val objectString = json.encodeToString(jsonElement)
80+
81+
println(objectString)
82+
println(json.decodeFromString<NullablePagingObject<T>>(objectString))
83+
json.decodeFromString(MapSerializer(t.first, t.second), this).apply { println("decoded") }
7384
}
85+
println("here4")
7486
return (map[innerObjectName] ?: if (arbitraryInnerNameAllowed) map.keys.firstOrNull()?.let { map[it] }
7587
?: error("") else error(""))
7688
.apply {
@@ -82,6 +94,7 @@ internal fun <T : Any> String.toPagingObject(
8294
}
8395
}
8496
}
97+
println("here5")
8598

8699
return try {
87100
val pagingObject = this.parseJson { json.decodeFromString(NullablePagingObject.serializer(tSerializer), this) }

src/commonMain/kotlin/com.adamratzman.spotify/utils/Platform.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ package com.adamratzman.spotify.utils
77
public enum class Platform {
88
JVM,
99
ANDROID,
10-
JS
10+
JS,
11+
NATIVE
1112
}
1213

1314
public expect val platform: Platform

src/commonMain/kotlin/com.adamratzman.spotify/utils/Utils.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package com.adamratzman.spotify.utils
33

44
import com.adamratzman.spotify.SpotifyException
55
import com.adamratzman.spotify.models.ResultEnum
6+
import kotlinx.datetime.Instant
7+
import kotlinx.datetime.TimeZone
8+
import kotlinx.datetime.toLocalDateTime
69
import kotlinx.serialization.json.JsonElement
710

811
public expect fun getCurrentTimeMs(): Long
@@ -22,4 +25,5 @@ internal suspend inline fun <T> catch(crossinline function: suspend () -> T): T?
2225
internal fun <T : ResultEnum> Array<T>.match(identifier: String) =
2326
firstOrNull { it.retrieveIdentifier().toString().equals(identifier, true) }
2427

25-
internal expect fun formatDate(format: String, date: Long): String
28+
internal fun formatDate(date: Long): String =
29+
Instant.fromEpochMilliseconds(date).toLocalDateTime(TimeZone.currentSystemDefault()).toString()

src/commonTest/kotlin/com.adamratzman/spotify/Common.kt

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,65 @@
11
/* Spotify Web API, Kotlin Wrapper; MIT License, 2017-2021; Original author: Adam Ratzman */
22
package com.adamratzman.spotify
33

4+
import com.autodesk.coroutineworker.CoroutineWorker
45
import kotlin.coroutines.CoroutineContext
56
import kotlin.test.assertTrue
67
import kotlinx.coroutines.CoroutineScope
78
import kotlinx.coroutines.CoroutineStart.LAZY
89
import kotlinx.coroutines.Deferred
910
import kotlinx.coroutines.Dispatchers
1011
import kotlinx.coroutines.GlobalScope
12+
import kotlinx.coroutines.MainScope
1113
import kotlinx.coroutines.async
14+
import kotlin.native.concurrent.ThreadLocal
1215

1316
val clientId = getEnvironmentVariable("SPOTIFY_CLIENT_ID")
1417
val clientSecret = getEnvironmentVariable("SPOTIFY_CLIENT_SECRET")
1518
val redirectUri = getEnvironmentVariable("SPOTIFY_REDIRECT_URI")
1619
val tokenString = getEnvironmentVariable("SPOTIFY_TOKEN_STRING")
1720

21+
22+
// https://github.com/Kotlin/kotlinx.coroutines/issues/1996#issuecomment-728562784
23+
expect fun runBlockingTest(block: suspend CoroutineScope.() -> Unit)
24+
expect val testCoroutineContext: CoroutineContext
25+
26+
@ThreadLocal
1827
var instantiationCompleted: Boolean = false
28+
@ThreadLocal
1929
private lateinit var apiBacking: GenericSpotifyApi
2030

2131
// https://github.com/Kotlin/kotlinx.coroutines/issues/706#issuecomment-429922811
22-
val spotifyApi: Deferred<GenericSpotifyApi?>
23-
get() = GlobalScope.async(Dispatchers.Unconfined, start = LAZY) {
24-
if (::apiBacking.isInitialized) return@async apiBacking
25-
when {
26-
tokenString?.isNotBlank() == true -> {
27-
spotifyClientApi {
28-
credentials {
29-
clientId = com.adamratzman.spotify.clientId
30-
clientSecret = com.adamratzman.spotify.clientSecret
31-
redirectUri = com.adamratzman.spotify.redirectUri
32-
}
33-
authorization {
34-
tokenString = com.adamratzman.spotify.tokenString
35-
}
36-
}.build().also { instantiationCompleted = true; apiBacking = it }
32+
suspend fun buildSpotifyApi() = when {
33+
tokenString?.isNotBlank() == true -> {
34+
spotifyClientApi {
35+
credentials {
36+
clientId = com.adamratzman.spotify.clientId
37+
clientSecret = com.adamratzman.spotify.clientSecret
38+
redirectUri = com.adamratzman.spotify.redirectUri
3739
}
38-
clientId?.isNotBlank() == true -> {
39-
spotifyAppApi {
40-
credentials {
41-
clientId = com.adamratzman.spotify.clientId
42-
clientSecret = com.adamratzman.spotify.clientSecret
43-
}
44-
}.build().also { instantiationCompleted = true; apiBacking = it }
40+
authorization {
41+
tokenString = com.adamratzman.spotify.tokenString
4542
}
46-
else -> null.also { instantiationCompleted = true }
43+
}.build().also { instantiationCompleted = true; apiBacking = it }
44+
}
45+
clientId?.isNotBlank() == true -> {
46+
println("building")
47+
spotifyAppApi {
48+
credentials {
49+
clientId = com.adamratzman.spotify.clientId
50+
clientSecret = com.adamratzman.spotify.clientSecret
51+
}
52+
}.build().also {
53+
println("built");instantiationCompleted = true; apiBacking = it;println("through seq")
4754
}
4855
}
56+
else -> null.also { instantiationCompleted = true }
57+
}?.also { print("done when") }
4958

5059
expect fun getEnvironmentVariable(name: String): String?
5160

5261
expect fun Exception.stackTrace()
5362

54-
// https://github.com/Kotlin/kotlinx.coroutines/issues/1996#issuecomment-728562784
55-
expect fun runBlockingTest(block: suspend CoroutineScope.() -> Unit)
56-
expect val testCoroutineContext: CoroutineContext
57-
5863
suspend inline fun <reified T : Throwable> assertFailsWithSuspend(crossinline block: suspend () -> Unit) {
5964
val noExceptionMessage = "Expected ${T::class.simpleName} exception to be thrown, but no exception was thrown."
6065
try {

0 commit comments

Comments
 (0)