diff --git a/buildSrc/src/main/kotlin/P.kt b/buildSrc/src/main/kotlin/P.kt index 4e604996e..9fdf6776a 100644 --- a/buildSrc/src/main/kotlin/P.kt +++ b/buildSrc/src/main/kotlin/P.kt @@ -47,8 +47,8 @@ fun isSnapshot(): Boolean = _isSnapshot @Suppress("MemberVisibilityCanBePrivate") sealed class P(override val group: String) : ProjectDetail() { companion object { - const val VERSION = "4.9.0" - const val NEXT_VERSION = "4.9.1" + const val VERSION = "4.10.0" + const val NEXT_VERSION = "4.10.0" const val SNAPSHOT_VERSION = "$VERSION-SNAPSHOT" const val NEXT_SNAPSHOT_VERSION = "$NEXT_VERSION-SNAPSHOT" diff --git a/simbot-api/api/simbot-api.api b/simbot-api/api/simbot-api.api index 2126040f4..52e66cfce 100644 --- a/simbot-api/api/simbot-api.api +++ b/simbot-api/api/simbot-api.api @@ -2195,11 +2195,15 @@ public abstract interface class love/forte/simbot/message/OfflineImage : love/fo public static final field Companion Llove/forte/simbot/message/OfflineImage$Companion; public abstract fun data ()[B public static fun ofBytes ([B)Llove/forte/simbot/message/OfflineImage; + public static fun ofFilePath (Ljava/lang/String;)Llove/forte/simbot/message/OfflineImage; + public static fun ofFilePath (Ljava/lang/String;[Ljava/lang/String;)Llove/forte/simbot/message/OfflineImage; public static fun ofResource (Llove/forte/simbot/resource/Resource;)Llove/forte/simbot/message/OfflineImage; } public final class love/forte/simbot/message/OfflineImage$Companion { public final fun ofBytes ([B)Llove/forte/simbot/message/OfflineImage; + public final fun ofFilePath (Ljava/lang/String;)Llove/forte/simbot/message/OfflineImage; + public final fun ofFilePath (Ljava/lang/String;[Ljava/lang/String;)Llove/forte/simbot/message/OfflineImage; public final fun ofResource (Llove/forte/simbot/resource/Resource;)Llove/forte/simbot/message/OfflineImage; } @@ -2528,14 +2532,15 @@ public abstract class love/forte/simbot/resource/AbstractJvmResourceValueResolve public final fun resolveURI (Llove/forte/simbot/resource/URIResource;Ljava/lang/Object;)V } -public abstract interface class love/forte/simbot/resource/ByteArrayResource : love/forte/simbot/resource/Resource { +public abstract interface class love/forte/simbot/resource/ByteArrayResource : love/forte/simbot/resource/Resource, love/forte/simbot/resource/SourceResource { public abstract fun data ()[B + public fun source ()Lkotlinx/io/Source; } public abstract interface annotation class love/forte/simbot/resource/ExperimentalIOResourceAPI : java/lang/annotation/Annotation { } -public abstract interface class love/forte/simbot/resource/FileResource : love/forte/simbot/resource/InputStreamResource, love/forte/simbot/resource/ReaderResource { +public abstract interface class love/forte/simbot/resource/FileResource : love/forte/simbot/resource/InputStreamResource, love/forte/simbot/resource/JvmSourceResource, love/forte/simbot/resource/ReaderResource { public fun data ()[B public abstract fun getFile ()Ljava/io/File; public fun inputStream ()Ljava/io/InputStream; @@ -2545,9 +2550,8 @@ public abstract interface class love/forte/simbot/resource/FileResource : love/f public abstract fun string (Ljava/nio/charset/Charset;)Ljava/lang/String; } -public abstract interface class love/forte/simbot/resource/InputStreamResource : love/forte/simbot/resource/Resource { - public fun data ()[B - public abstract fun inputStream ()Ljava/io/InputStream; +public abstract interface class love/forte/simbot/resource/InputStreamResource : love/forte/simbot/resource/SourceResource { + public fun inputStream ()Ljava/io/InputStream; } public abstract interface class love/forte/simbot/resource/JvmResourceResolver : love/forte/simbot/resource/ResourceResolver { @@ -2572,6 +2576,9 @@ public abstract interface class love/forte/simbot/resource/JvmResourceValueResol public abstract fun resolveURINotFileScheme (Ljava/net/URI;Ljava/lang/Object;)V } +public abstract interface class love/forte/simbot/resource/JvmSourceResource : love/forte/simbot/resource/SourceResource { +} + public abstract interface class love/forte/simbot/resource/JvmStringReadableResource : love/forte/simbot/resource/StringReadableResource { public static final field Companion Llove/forte/simbot/resource/JvmStringReadableResource$Companion; public static final field DEFAULT_CHARSET Ljava/nio/charset/Charset; @@ -2582,7 +2589,7 @@ public abstract interface class love/forte/simbot/resource/JvmStringReadableReso public final class love/forte/simbot/resource/JvmStringReadableResource$Companion { } -public abstract interface class love/forte/simbot/resource/PathResource : love/forte/simbot/resource/InputStreamResource, love/forte/simbot/resource/ReaderResource { +public abstract interface class love/forte/simbot/resource/PathResource : love/forte/simbot/resource/InputStreamResource, love/forte/simbot/resource/JvmSourceResource, love/forte/simbot/resource/ReaderResource { public fun data ()[B public abstract fun getPath ()Ljava/nio/file/Path; public abstract fun inputStream ()Ljava/io/InputStream; @@ -2622,6 +2629,7 @@ public final class love/forte/simbot/resource/ResourceResolver$Companion { } public final class love/forte/simbot/resource/Resources { + public static final fun inputStream (Llove/forte/simbot/resource/SourceResource;)Ljava/io/InputStream; public static final fun valueOf (Ljava/io/File;)Llove/forte/simbot/resource/FileResource; public static final fun valueOf (Ljava/io/File;Ljava/nio/charset/Charset;)Llove/forte/simbot/resource/FileResource; public static final fun valueOf (Ljava/lang/String;)Llove/forte/simbot/resource/StringResource; @@ -2631,14 +2639,29 @@ public final class love/forte/simbot/resource/Resources { public static final fun valueOf (Ljava/net/URL;Ljava/nio/charset/Charset;)Llove/forte/simbot/resource/URIResource; public static final fun valueOf (Ljava/nio/file/Path;Ljava/nio/charset/Charset;[Ljava/nio/file/OpenOption;)Llove/forte/simbot/resource/PathResource; public static final fun valueOf (Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Llove/forte/simbot/resource/PathResource; + public static final fun valueOf (Lkotlinx/io/files/Path;)Llove/forte/simbot/resource/SourceResource; public static final fun valueOf ([B)Llove/forte/simbot/resource/ByteArrayResource; public static synthetic fun valueOf$default (Ljava/io/File;Ljava/nio/charset/Charset;ILjava/lang/Object;)Llove/forte/simbot/resource/FileResource; public static synthetic fun valueOf$default (Ljava/net/URI;Ljava/nio/charset/Charset;ILjava/lang/Object;)Llove/forte/simbot/resource/URIResource; public static synthetic fun valueOf$default (Ljava/net/URL;Ljava/nio/charset/Charset;ILjava/lang/Object;)Llove/forte/simbot/resource/URIResource; public static synthetic fun valueOf$default (Ljava/nio/file/Path;Ljava/nio/charset/Charset;[Ljava/nio/file/OpenOption;ILjava/lang/Object;)Llove/forte/simbot/resource/PathResource; + public static final fun valueOfInputStreamProvider (Lkotlin/jvm/functions/Function0;)Llove/forte/simbot/resource/SourceResource; + public static final fun valueOfPath (Ljava/lang/String;)Llove/forte/simbot/resource/SourceResource; + public static final fun valueOfPath (Ljava/lang/String;[Ljava/lang/String;)Llove/forte/simbot/resource/SourceResource; + public static final fun valueOfSourceProvider (Lkotlin/jvm/functions/Function0;)Llove/forte/simbot/resource/SourceResource; } -public abstract interface class love/forte/simbot/resource/StringReadableResource : love/forte/simbot/resource/Resource { +public abstract interface annotation class love/forte/simbot/resource/ScheduledDeprecatedResourceApi : java/lang/annotation/Annotation { +} + +public abstract interface class love/forte/simbot/resource/SourceResource : love/forte/simbot/resource/Resource { + public fun data ()[B + public abstract fun source ()Lkotlinx/io/Source; +} + +public abstract interface class love/forte/simbot/resource/StringReadableResource : love/forte/simbot/resource/SourceResource { + public fun data ()[B + public fun source ()Lkotlinx/io/Source; public abstract fun string ()Ljava/lang/String; } @@ -2646,7 +2669,7 @@ public abstract interface class love/forte/simbot/resource/StringResource : love public abstract fun string ()Ljava/lang/String; } -public abstract interface class love/forte/simbot/resource/URIResource : love/forte/simbot/resource/InputStreamResource, love/forte/simbot/resource/JvmStringReadableResource { +public abstract interface class love/forte/simbot/resource/URIResource : love/forte/simbot/resource/InputStreamResource, love/forte/simbot/resource/JvmSourceResource, love/forte/simbot/resource/JvmStringReadableResource { public abstract fun getUri ()Ljava/net/URI; public abstract fun inputStream ()Ljava/io/InputStream; public fun string ()Ljava/lang/String; diff --git a/simbot-api/src/commonMain/kotlin/love/forte/simbot/message/OfflineImageResolver.kt b/simbot-api/src/commonMain/kotlin/love/forte/simbot/message/OfflineImageResolver.kt index 00ca39c4f..756117fc7 100644 --- a/simbot-api/src/commonMain/kotlin/love/forte/simbot/message/OfflineImageResolver.kt +++ b/simbot-api/src/commonMain/kotlin/love/forte/simbot/message/OfflineImageResolver.kt @@ -23,11 +23,8 @@ package love.forte.simbot.message -import love.forte.simbot.resource.ByteArrayResource -import love.forte.simbot.resource.Resource -import love.forte.simbot.resource.ResourceResolver +import love.forte.simbot.resource.* import love.forte.simbot.resource.ResourceResolver.Companion.resolve -import love.forte.simbot.resource.StringResource import kotlin.jvm.JvmStatic @@ -76,6 +73,7 @@ public interface OfflineImageResolver { * 继承 [OfflineImageResolver] 和 [ResourceResolver], * 对其中可能出现的实际内容物(例如 [ByteArray] 或 [String])进行处理。 */ +@ScheduledDeprecatedResourceApi public interface OfflineImageValueResolver : OfflineImageResolver, ResourceResolver { diff --git a/simbot-api/src/commonMain/kotlin/love/forte/simbot/message/StandardMessages.kt b/simbot-api/src/commonMain/kotlin/love/forte/simbot/message/StandardMessages.kt index ef24a1a25..104b787eb 100644 --- a/simbot-api/src/commonMain/kotlin/love/forte/simbot/message/StandardMessages.kt +++ b/simbot-api/src/commonMain/kotlin/love/forte/simbot/message/StandardMessages.kt @@ -30,9 +30,14 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import love.forte.simbot.common.id.ID import love.forte.simbot.common.id.IDContainer +import love.forte.simbot.message.At.Companion.equals +import love.forte.simbot.message.At.Companion.hashCode import love.forte.simbot.message.OfflineImage.Companion.toOfflineImage import love.forte.simbot.message.Text.Companion.of -import love.forte.simbot.resource.* +import love.forte.simbot.resource.ByteArrayResource +import love.forte.simbot.resource.Resource +import love.forte.simbot.resource.ResourceBase64Serializer +import love.forte.simbot.resource.fileResource import kotlin.io.encoding.ExperimentalEncodingApi import kotlin.js.JsName import kotlin.jvm.* @@ -297,12 +302,16 @@ public interface UrlAwareImage : Image, UrlAwareMessage { * * “离线”主要表示此图片并未上传到某个目标平台中,也没有与某个远程服务器互相对应的唯一标识。 * - * @see OfflineImage.toOfflineImage + * 离线图片消息由本地构建,不会来自远端服务器或事件内。 + * 离线图片消息无法保证可序列化性,尽可能避免对其进行序列化(包括作为 [Messages] 的元素时)。 + * + * @see ByteArray.toOfflineImage + * @see Resource.toOfflineImage * * @see OfflineByteArrayImage - * @see SimpleOfflineResourceImage + * @see OfflineResourceImage */ -public interface OfflineImage : Image { +public sealed interface OfflineImage : Image { /** * 得到图片的二进制数据 */ @@ -348,7 +357,6 @@ public interface OfflineImage : Image { */ @JvmStatic @JvmName("ofFilePath") - @ExperimentalIOResourceAPI public fun fileOfflineImage(filePath: String): OfflineImage = fileResource(filePath).toOfflineResourceImage() @@ -363,7 +371,6 @@ public interface OfflineImage : Image { * @since 4.7.0 */ @JvmStatic - @ExperimentalIOResourceAPI @JvmName("ofFilePath") public fun fileOfflineImage(base: String, vararg parts: String): OfflineImage = fileResource(base, *parts).toOfflineResourceImage() diff --git a/simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/Resource.kt b/simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/Resource.kt index bd4a44652..4dfa682c0 100644 --- a/simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/Resource.kt +++ b/simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/Resource.kt @@ -26,6 +26,8 @@ package love.forte.simbot.resource +import kotlinx.io.Buffer +import kotlinx.io.Source import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor @@ -46,6 +48,17 @@ import kotlin.jvm.JvmName * JVM 中的部分扩展、辅助API通过静态类 `Resources` 提供, * 例如 `Resources.valueOf(...)`。 * + * ## 有限范围 + * + * 从 `v4.10.0` 开始,[Resource] 接口转为 `sealed` 并有两个明确的子类型分支: + * - [ByteArrayResource] + * - [SourceResource] + * + * [ByteArrayResource] 代表一个可以**直接**使用 [ByteArray] 进行表示的资源,它也同样可以表示为 [SourceResource]。 + * + * 其中,[SourceResource] 借助 `kotlinx-io` 库所提供的能力统一多平台的IO相关API(例如文件系统相关)。 + * 如果你想要基于文件系统或其他与IO相关的内容构建一个 [Resource],则参考 [SourceResource]。 + * * ## 序列化 * * [Resource] 提供了一个基于 [Base64] 进行序列化操作的 [ResourceBase64Serializer]。 @@ -59,9 +72,7 @@ import kotlin.jvm.JvmName * * @author ForteScarlet */ -public interface Resource { - // TODO become `sealed` for ByteArrayResource and SourceResource. - +public sealed interface Resource { /** * 读取此资源的字节数据。 * @@ -83,11 +94,15 @@ public fun ByteArray.toResource(): ByteArrayResource = ByteArrayResourceImpl(thi * * @author forte */ -public interface ByteArrayResource : Resource { +public interface ByteArrayResource : Resource, SourceResource { /** * 获取到字节数组结果。 */ override fun data(): ByteArray + + override fun source(): Source { + return Buffer().apply { write(data()) } + } } /** @@ -147,12 +162,18 @@ private data class ByteArrayResourceImpl(private val raw: ByteArray) : ByteArray * 一个可以读取到 [String] 内容物的拓展类型。 * 是其他 [Resource] 类型的附加能力,但不属于一个标准的 [Resource] 类型。 */ -public interface StringReadableResource : Resource { +public interface StringReadableResource : SourceResource { /** * 读取此资源的 [String] 内容。 */ @Throws(Exception::class) public fun string(): String + + override fun data(): ByteArray = string().encodeToByteArray() + + override fun source(): Source { + return Buffer().apply { write(data()) } + } } /** diff --git a/simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/ResourceResolver.kt b/simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/ResourceResolver.kt index 60d49556b..ac50af881 100644 --- a/simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/ResourceResolver.kt +++ b/simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/ResourceResolver.kt @@ -23,8 +23,23 @@ package love.forte.simbot.resource +import kotlin.annotation.AnnotationRetention.BINARY +import kotlin.annotation.AnnotationTarget.CLASS +import kotlin.annotation.AnnotationTarget.FUNCTION import kotlin.jvm.JvmStatic +/** + * 计划被废弃的与 [Resource] 相关的API + */ +@RequiresOptIn( + message = "计划被废弃的与 `Resource` 相关的API. 详见 " + + "`love.forte.simbot.resource.ResourceResolver` 和 " + + "`love.forte.simbot.resource.Resource` 中的有关说明。" +) +@Retention(BINARY) +@Target(CLASS, FUNCTION) +@MustBeDocumented +public annotation class ScheduledDeprecatedResourceApi /** * 使用 [ResourceResolver] 分析处理一个 [Resource]. @@ -33,8 +48,14 @@ import kotlin.jvm.JvmStatic * * 在 JVM 平台会提供一个具有更多能力的类型。 * + * Note: 由于[Resource]现在已经通过 `sealed` 限制了子类型范围, + * 因此可以直接使用 [ByteArrayResource] 或 [SourceResource]。 + * 得益于 `kotlinx-io`,明确 resolve 多平台(尤其是JVM平台)下的独特类型的情况已经不多了。 + * [ResourceResolver] 可能会在未来废弃, 且现在开始不再建议使用。 + * * @author ForteScarlet */ +@ScheduledDeprecatedResourceApi public interface ResourceResolver { /** * 处理一个未知的 [Resource] 类型的 resource. diff --git a/simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/IOResources.kt b/simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/SourceResource.kt similarity index 86% rename from simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/IOResources.kt rename to simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/SourceResource.kt index 63246cd23..3c2125959 100644 --- a/simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/IOResources.kt +++ b/simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/SourceResource.kt @@ -78,7 +78,6 @@ public annotation class ExperimentalIOResourceAPI * @since 4.7.0 */ @JvmName("valueOfPath") -@ExperimentalIOResourceAPI public fun fileResource(filePath: String): SourceResource { return Path(filePath).toResource() } @@ -94,7 +93,6 @@ public fun fileResource(filePath: String): SourceResource { * @since 4.7.0 */ @JvmName("valueOfPath") -@ExperimentalIOResourceAPI public fun fileResource(base: String, vararg parts: String): SourceResource { return Path(base, *parts).toResource() } @@ -110,19 +108,36 @@ public fun fileResource(base: String, vararg parts: String): SourceResource { * @since 4.8.0 */ @JvmName("valueOf") -@ExperimentalIOResourceAPI public fun Path.toResource(): SourceResource { return FilePathResource(this) } +/** + * 提供一个用于产生 [Source] 的供应函数 [provider], + * 并得到一个 [SourceResource]。 + * + * 得到的结果每次使用 [SourceResource.source] 都会通过 [provider] + * 获取一个 [Source]。[Source] 应当由使用者决定关闭时机,而不是在 [provider] 中。 + * + * @since 4.10.0 + */ +@JvmName("valueOfSourceProvider") +public fun sourceResource(provider: () -> Source): SourceResource { + return SourceResourceImpl(provider) +} + + /** * 一个可以得到 [kotlinx.io.Source] 的 [Resource]。 * + * 在 JVM 平台中 `Resources` 中会提供更多基于JVM文件系统构建 [SourceResource] 的 API。 + * * @see fileResource + * @see sourceResource + * @see Path.toResource * * @since 4.7.0 */ -@ExperimentalIOResourceAPI public interface SourceResource : Resource { /** * 得到一个用于本次数据读取的 [Source]. @@ -154,7 +169,6 @@ public interface SourceResource : Resource { override fun data(): ByteArray = source().use { it.readByteArray() } } -@ExperimentalIOResourceAPI private data class FilePathResource(val path: Path) : SourceResource { private val source get() = SystemFileSystem.source(path) @@ -163,3 +177,9 @@ private data class FilePathResource(val path: Path) : SourceResource { override fun source(): Source = source.buffered() } +/** + * @since 4.10.0 + */ +private data class SourceResourceImpl(val provider: () -> Source) : SourceResource { + override fun source(): Source = provider() +} diff --git a/simbot-api/src/commonTest/kotlin/message/OfflineImageResolverTests.kt b/simbot-api/src/commonTest/kotlin/message/OfflineImageResolverTests.kt deleted file mode 100644 index afaf5cd65..000000000 --- a/simbot-api/src/commonTest/kotlin/message/OfflineImageResolverTests.kt +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2024. ForteScarlet. - * - * Project https://github.com/simple-robot/simpler-robot - * Email ForteScarlet@163.com - * - * This file is part of the Simple Robot Library (Alias: simple-robot, simbot, etc.). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Lesser GNU General Public License for more details. - * - * You should have received a copy of the Lesser GNU General Public License - * along with this program. If not, see . - * - */ - -package message - -import kotlinx.coroutines.suspendCancellableCoroutine -import kotlinx.coroutines.test.runTest -import love.forte.simbot.message.OfflineByteArrayImage -import love.forte.simbot.message.OfflineImage -import love.forte.simbot.message.OfflineImage.Companion.toOfflineImage -import love.forte.simbot.message.OfflineImageResolver -import love.forte.simbot.message.OfflineImageResolver.Companion.resolve -import love.forte.simbot.message.OfflineResourceImage -import love.forte.simbot.resource.Resource -import kotlin.coroutines.Continuation -import kotlin.coroutines.resume -import kotlin.test.Test -import kotlin.test.assertEquals - - -/** - * - * @author ForteScarlet - */ -class OfflineImageResolverTests { - private enum class Value { - UKN, - BA, - R - } - - @Test - fun offlineImageTest() = runTest { - val resolver = object : OfflineImageResolver> { - override fun resolveUnknown(image: OfflineImage, context: Continuation) { - context.resume(Value.UKN) - } - - override fun resolveByteArray(image: OfflineByteArrayImage, context: Continuation) { - context.resume(Value.BA) - } - - override fun resolveResource(image: OfflineResourceImage, context: Continuation) { - context.resume(Value.R) - } - } - assertEquals( - Value.UKN, - suspendCancellableCoroutine { c -> - resolver.resolve( - object : OfflineImage { - override fun data(): ByteArray = byteArrayOf() - }, - c - ) - } - ) - assertEquals( - Value.BA, - suspendCancellableCoroutine { c -> - resolver.resolve( - byteArrayOf().toOfflineImage(), - c - ) - } - ) - assertEquals( - Value.R, - suspendCancellableCoroutine { c -> - resolver.resolve( - object : Resource { - override fun data(): ByteArray = byteArrayOf() - }.toOfflineImage(), - c - ) - } - ) - } - -} diff --git a/simbot-api/src/commonTest/kotlin/resource/ResourceResolverTests.kt b/simbot-api/src/commonTest/kotlin/resource/ResourceResolverTests.kt deleted file mode 100644 index 037fe9c7d..000000000 --- a/simbot-api/src/commonTest/kotlin/resource/ResourceResolverTests.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2024. ForteScarlet. - * - * Project https://github.com/simple-robot/simpler-robot - * Email ForteScarlet@163.com - * - * This file is part of the Simple Robot Library (Alias: simple-robot, simbot, etc.). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Lesser GNU General Public License for more details. - * - * You should have received a copy of the Lesser GNU General Public License - * along with this program. If not, see . - * - */ - -package resource - -import love.forte.simbot.resource.* -import love.forte.simbot.resource.ResourceResolver.Companion.resolve -import kotlin.test.Test -import kotlin.test.assertEquals - - -/** - * - * @author ForteScarlet - */ -class ResourceResolverTests { - private enum class Value { - UKN, - BA, - S - } - - @Test - fun resourceResolverTest() { - var value: Value? = null - - val resource = object : ResourceResolver { - override fun resolveUnknown(resource: Resource, context: Unit) { - value = Value.UKN - } - - override fun resolveByteArray(resource: ByteArrayResource, context: Unit) { - value = Value.BA - } - - override fun resolveString(resource: StringResource, context: Unit) { - value = Value.S - } - } - - resource.resolve( - byteArrayOf().toResource(), - Unit - ) - - assertEquals(Value.BA, value) - - resource.resolve( - "".toStringResource(), - Unit - ) - - assertEquals(Value.S, value) - - resource.resolve( - object : Resource { - override fun data(): ByteArray { - return byteArrayOf() - } - }, - Unit - ) - - assertEquals(Value.UKN, value) - - } - -} diff --git a/simbot-api/src/jvmMain/kotlin/love/forte/simbot/message/OfflineImageResolver.jvm.kt b/simbot-api/src/jvmMain/kotlin/love/forte/simbot/message/OfflineImageResolver.jvm.kt index 995aba9dd..e9b7b9cb4 100644 --- a/simbot-api/src/jvmMain/kotlin/love/forte/simbot/message/OfflineImageResolver.jvm.kt +++ b/simbot-api/src/jvmMain/kotlin/love/forte/simbot/message/OfflineImageResolver.jvm.kt @@ -71,6 +71,7 @@ public interface JvmOfflineImageResolver : OfflineImageResolver { * 实现 [JvmOfflineImageResolver] 和 [JvmResourceResolver], * 对其中可能出现的实际内容物(例如 [File] 或 [Path])进行处理。 */ +@ScheduledDeprecatedResourceApi public abstract class JvmOfflineImageValueResolver : OfflineImageValueResolver, JvmOfflineImageResolver, diff --git a/simbot-api/src/jvmMain/kotlin/love/forte/simbot/resource/Resource.jvm.kt b/simbot-api/src/jvmMain/kotlin/love/forte/simbot/resource/Resource.jvm.kt index 91e5a1958..334393a96 100644 --- a/simbot-api/src/jvmMain/kotlin/love/forte/simbot/resource/Resource.jvm.kt +++ b/simbot-api/src/jvmMain/kotlin/love/forte/simbot/resource/Resource.jvm.kt @@ -27,6 +27,7 @@ package love.forte.simbot.resource import kotlinx.io.Source +import kotlinx.io.asInputStream import kotlinx.io.asSource import kotlinx.io.buffered import love.forte.simbot.resource.JvmStringReadableResource.Companion.DEFAULT_CHARSET @@ -38,31 +39,59 @@ import java.net.URL import java.nio.charset.Charset import java.nio.file.OpenOption import java.nio.file.Path -import kotlin.Throws import kotlin.io.path.inputStream import kotlin.io.path.readBytes import kotlin.io.path.reader +/** + * 从 [SourceResource] 中通过 [Source] 获取一个 [InputStream]。 + * + * @since 4.10.0 + */ +@Throws(Exception::class) +public fun SourceResource.inputStream(): InputStream { + return source().asInputStream() +} + +/** + * 提供JVM平台独特实现的类型,与 [SourceResource] 内容相同。 + * + * @see FileResource + * @see PathResource + * @see URIResource + */ +public sealed interface JvmSourceResource : SourceResource + /** * 能够获取到 [InputStream] 资源的 [Resource] 扩展实现。 * + * Deprecated since v4.10.0: 直接通过 [SourceResource.inputStream] + * 或 [Source.asInputStream] 即可将 [SourceResource] 中的 [Source] 转化为 [InputStream]。 + * + * ```kotlin + * val input1 = resource.inputStream() + * val input2 = resource.source().asInputStream() + * ``` + * + * Java 中分别对应 `Resources.inputStream(sourceResource)` + * 和 `SourcesJvmKt.asInputStream(source)`。 + * + * ```java + * var input1 = Resources,inputStream(resource); + * var input2 = SourcesJvmKt.asInputStream(resource.source()); + * ``` + * * @author forte */ -@OptIn(ExperimentalIOResourceAPI::class) -public interface InputStreamResource : Resource { - /** - * 读取当前资源的所有字节数据。 - * 默认通过 [inputStream] 读取。 - * - */ - @Throws(IOException::class) - override fun data(): ByteArray = inputStream().use { it.readAllBytes() } - +@Deprecated( + "Just use `SourceResource.inputStream()` to get InputStream from Source" +) +public interface InputStreamResource : SourceResource { /** * 获取可用于读取当前资源数据的输入流。 */ @Throws(IOException::class) - public fun inputStream(): InputStream + public fun inputStream(): InputStream = source().asInputStream() } /** @@ -84,7 +113,6 @@ public interface JvmStringReadableResource : StringReadableResource { @Throws(IOException::class) public fun string(charset: Charset): String - public companion object { /** * 默认编码格式: [Charsets.UTF_8] @@ -97,8 +125,14 @@ public interface JvmStringReadableResource : StringReadableResource { /** * 能够获取到 [Reader] 资源的 [Resource] 扩展实现。 * + * Deprecated since v4.10.0. + * 可以直接获取到 [InputStream], 进而直接获取到 reader, 此接口意义不大。 + * * @author forte */ +@Deprecated( + "Just use `SourceResource.inputStream()` to get InputStream from Source" +) public interface ReaderResource : JvmStringReadableResource { /** * 读取当前资源的字符串数据。 @@ -126,7 +160,11 @@ public interface ReaderResource : JvmStringReadableResource { * * @author forte */ -public interface FileResource : InputStreamResource, ReaderResource { +@Suppress("DEPRECATION") +public interface FileResource : + JvmSourceResource, + InputStreamResource, + ReaderResource { /** * 与此资源关联的 [File] */ @@ -189,7 +227,6 @@ public interface FileResource : InputStreamResource, ReaderResource { public fun File.toResource(charset: Charset = DEFAULT_CHARSET): FileResource = FileResourceImpl(this, charset) -@OptIn(ExperimentalIOResourceAPI::class) private data class FileResourceImpl(override val file: File, private val charset: Charset) : FileResource, SourceResource { override fun string(): String = string(charset) @@ -227,7 +264,11 @@ private data class FileResourceImpl(override val file: File, private val charset * * @author forte */ -public interface PathResource : InputStreamResource, ReaderResource { +@Suppress("DEPRECATION") +public interface PathResource : + JvmSourceResource, + InputStreamResource, + ReaderResource { /** * 与此资源关联的 [Path] */ @@ -278,7 +319,6 @@ public fun Path.toResource( ): PathResource = PathResourceImpl(this, charset, options) -@OptIn(ExperimentalIOResourceAPI::class) private data class PathResourceImpl( override val path: Path, private val charset: Charset, @@ -330,7 +370,11 @@ private data class PathResourceImpl( * * @author forte */ -public interface URIResource : InputStreamResource, JvmStringReadableResource { +@Suppress("DEPRECATION") +public interface URIResource : + JvmSourceResource, + InputStreamResource, + JvmStringReadableResource { /** * 与此资源关联的 [URI] */ @@ -400,7 +444,6 @@ public fun URL.toResource(charset: Charset = DEFAULT_CHARSET): URIResource = public fun URI.toResource(charset: Charset = DEFAULT_CHARSET): URIResource = URIResourceImpl(this, charset, null) -@OptIn(ExperimentalIOResourceAPI::class) private class URIResourceImpl(override val uri: URI, val charset: Charset, private var url: URL? = null) : URIResource, SourceResource { @@ -419,3 +462,18 @@ private class URIResourceImpl(override val uri: URI, val charset: Charset, priva override fun toString(): String = "URIResource(uri=$uri, charset=$charset)" } + +/** + * 提供一个用于产生 [InputStream] 的供应函数 [provider], + * 并得到一个 [SourceResource]。 + * + * 得到的结果每次使用 [SourceResource.source] 都会通过 [provider] + * 获取一个 [Source]。[Source] 应当由使用者决定关闭时机,而不是在 [provider] 中。 + * + * 函数本质上使用 [sourceResource]。 + * + * @since v4.10.0 + */ +@JvmName("valueOfInputStreamProvider") +public fun inputStreamResource(provider: () -> InputStream): SourceResource = + sourceResource { provider().asSource().buffered() } diff --git a/simbot-api/src/jvmMain/kotlin/love/forte/simbot/resource/ResourceResolver.jvm.kt b/simbot-api/src/jvmMain/kotlin/love/forte/simbot/resource/ResourceResolver.jvm.kt index 27b3f9671..a11825580 100644 --- a/simbot-api/src/jvmMain/kotlin/love/forte/simbot/resource/ResourceResolver.jvm.kt +++ b/simbot-api/src/jvmMain/kotlin/love/forte/simbot/resource/ResourceResolver.jvm.kt @@ -32,6 +32,7 @@ import kotlin.io.path.toPath * JVM 平台下 [ResourceResolver] 的进一步扩展类型。 * */ +@ScheduledDeprecatedResourceApi public interface JvmResourceResolver : ResourceResolver { /** * 处理类型为 [FileResource] 的 resource. @@ -71,6 +72,7 @@ public interface JvmResourceResolver : ResourceResolver { /** * [JvmResourceResolver] 的更安全的抽象类型,将 [resolveUnknown] 固定为 `final`。 */ +@ScheduledDeprecatedResourceApi public abstract class AbstractJvmResourceResolver : JvmResourceResolver { final override fun resolveUnknown(resource: Resource, context: C) { super.resolveUnknown(resource, context) @@ -81,6 +83,7 @@ public abstract class AbstractJvmResourceResolver : JvmResourceResolver { * 基于 Resource 中的值进行处理。 * 其中,如果 [URI.scheme] == `"file"` 则会被处理为 [Path]. */ +@ScheduledDeprecatedResourceApi public interface JvmResourceValueResolver : JvmResourceResolver { /** * 解析 [FileResource] 类型的 resource, @@ -172,6 +175,7 @@ public interface JvmResourceValueResolver : JvmResourceResolver { * [JvmResourceValueResolver] 的更安全的抽象类型, * 会将部分直接解析 [Resource] 的函数固定为 `final`。 */ +@ScheduledDeprecatedResourceApi public abstract class AbstractJvmResourceValueResolver : JvmResourceValueResolver { final override fun resolveFile(resource: FileResource, context: C) { super.resolveFile(resource, context) diff --git a/simbot-api/src/jvmTest/kotlin/message/OfflineImageResolverTests.jvm.kt b/simbot-api/src/jvmTest/kotlin/message/OfflineImageResolverTests.jvm.kt deleted file mode 100644 index e21268ff3..000000000 --- a/simbot-api/src/jvmTest/kotlin/message/OfflineImageResolverTests.jvm.kt +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) 2024. ForteScarlet. - * - * Project https://github.com/simple-robot/simpler-robot - * Email ForteScarlet@163.com - * - * This file is part of the Simple Robot Library (Alias: simple-robot, simbot, etc.). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Lesser GNU General Public License for more details. - * - * You should have received a copy of the Lesser GNU General Public License - * along with this program. If not, see . - * - */ - -package message - -import kotlinx.coroutines.suspendCancellableCoroutine -import kotlinx.coroutines.test.runTest -import love.forte.simbot.message.JvmOfflineImageValueResolver -import love.forte.simbot.message.OfflineFileImage.Companion.toOfflineImage -import love.forte.simbot.message.OfflineImage -import love.forte.simbot.message.OfflineImage.Companion.toOfflineImage -import love.forte.simbot.message.OfflineImageResolver.Companion.resolve -import love.forte.simbot.message.OfflinePathImage.Companion.toOfflineImage -import love.forte.simbot.message.OfflineURIImage.Companion.toOfflineImage -import love.forte.simbot.resource.Resource -import love.forte.simbot.resource.toResource -import java.io.File -import java.net.URI -import java.nio.file.Files -import java.nio.file.Path -import kotlin.coroutines.Continuation -import kotlin.coroutines.resume -import kotlin.test.Test -import kotlin.test.assertEquals - - -/** - * - * @author ForteScarlet - */ -class JvmOfflineImageResolverTests { - private enum class Value { - UKN_I, - UKN_R, - BA, - S, - F, - P, - U - } - - @Test - fun jvmOfflineImageValueResolverTest() = runTest { - val path = Files.createTempFile("PRE", "SUF") - path.toFile().deleteOnExit() - - val resolver = object : JvmOfflineImageValueResolver>() { - override fun resolveUnknownInternal(image: OfflineImage, context: Continuation) { - context.resume(Value.UKN_I) - } - - override fun resolveUnknownInternal(resource: Resource, context: Continuation) { - context.resume(Value.UKN_R) - } - - override fun resolveByteArray(byteArray: ByteArray, context: Continuation) { - context.resume(Value.BA) - } - - override fun resolveString(string: String, context: Continuation) { - context.resume(Value.S) - } - - override fun resolveFile(file: File, context: Continuation) { - context.resume(Value.F) - } - - override fun resolvePath(path: Path, context: Continuation) { - context.resume(Value.P) - } - - override fun resolveURINotFileScheme(uri: URI, context: Continuation) { - context.resume(Value.U) - } - } - - assertEquals( - Value.UKN_I, - suspendCancellableCoroutine { c -> - resolver.resolve( - object : OfflineImage { - override fun data(): ByteArray = byteArrayOf() - }, - c - ) - } - ) - assertEquals( - Value.UKN_R, - suspendCancellableCoroutine { c -> - resolver.resolve( - object : Resource { - override fun data(): ByteArray = byteArrayOf() - }.toOfflineImage(), - c - ) - } - ) - assertEquals( - Value.F, - suspendCancellableCoroutine { c -> - resolver.resolve( - path.toFile().toOfflineImage(), - c - ) - } - ) - assertEquals( - Value.F, - suspendCancellableCoroutine { c -> - resolver.resolve( - path.toFile().toResource().toOfflineImage(), - c - ) - } - ) - assertEquals( - Value.P, - suspendCancellableCoroutine { c -> - resolver.resolve( - path.toOfflineImage(), - c - ) - } - ) - assertEquals( - Value.P, - suspendCancellableCoroutine { c -> - resolver.resolve( - path.toResource().toOfflineImage(), - c - ) - } - ) - assertEquals( - Value.U, - suspendCancellableCoroutine { c -> - resolver.resolve( - URI.create("https://baidu.com").toOfflineImage(), - c - ) - } - ) - assertEquals( - Value.U, - suspendCancellableCoroutine { c -> - resolver.resolve( - URI.create("https://baidu.com").toResource().toOfflineImage(), - c - ) - } - ) - } - -} diff --git a/simbot-api/src/jvmTest/kotlin/resource/ResourceResolverTests.jvm.kt b/simbot-api/src/jvmTest/kotlin/resource/ResourceResolverTests.jvm.kt deleted file mode 100644 index 586dcfe7b..000000000 --- a/simbot-api/src/jvmTest/kotlin/resource/ResourceResolverTests.jvm.kt +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2024. ForteScarlet. - * - * Project https://github.com/simple-robot/simpler-robot - * Email ForteScarlet@163.com - * - * This file is part of the Simple Robot Library (Alias: simple-robot, simbot, etc.). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Lesser GNU General Public License for more details. - * - * You should have received a copy of the Lesser GNU General Public License - * along with this program. If not, see . - * - */ - -package resource - -import kotlinx.coroutines.suspendCancellableCoroutine -import kotlinx.coroutines.test.runTest -import love.forte.simbot.resource.AbstractJvmResourceValueResolver -import love.forte.simbot.resource.Resource -import love.forte.simbot.resource.ResourceResolver.Companion.resolve -import love.forte.simbot.resource.toResource -import love.forte.simbot.resource.toStringResource -import java.io.File -import java.net.URI -import java.nio.file.Files -import java.nio.file.Path -import kotlin.coroutines.Continuation -import kotlin.coroutines.resume -import kotlin.test.Test -import kotlin.test.assertEquals - -/* - * Copyright (c) 2024. ForteScarlet. - * - * Project https://github.com/simple-robot/simpler-robot - * Email ForteScarlet@163.com - * - * This file is part of the Simple Robot Library (Alias: simple-robot, simbot, etc.). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Lesser GNU General Public License for more details. - * - * You should have received a copy of the Lesser GNU General Public License - * along with this program. If not, see . - * - */ - -/** - * - * @author ForteScarlet - */ -class ResourceResolverTestsJvm { - private enum class Value { - UKN, - BA, - S, - F, - P, - U - } - - @Test - fun jvmResolverTest() = runTest { - val path = Files.createTempFile("PRE", "SUF") - path.toFile().deleteOnExit() - - val resource = object : AbstractJvmResourceValueResolver>() { - override fun resolveUnknownInternal(resource: Resource, context: Continuation) { - context.resume(Value.UKN) - } - - override fun resolveFile(file: File, context: Continuation) { - context.resume(Value.F) - } - - override fun resolvePath(path: Path, context: Continuation) { - context.resume(Value.P) - } - - override fun resolveByteArray(byteArray: ByteArray, context: Continuation) { - context.resume(Value.BA) - } - - override fun resolveString(string: String, context: Continuation) { - context.resume(Value.S) - } - - override fun resolveURINotFileScheme(uri: URI, context: Continuation) { - context.resume(Value.U) - } - } - - assertEquals( - Value.P, - suspendCancellableCoroutine { c -> - resource.resolve(path.toResource(), c) - } - ) - assertEquals( - Value.F, - suspendCancellableCoroutine { c -> - resource.resolve(path.toFile().toResource(), c) - } - ) - assertEquals( - Value.BA, - suspendCancellableCoroutine { c -> - resource.resolve(byteArrayOf().toResource(), c) - } - ) - assertEquals( - Value.S, - suspendCancellableCoroutine { c -> - resource.resolve("".toStringResource(), c) - } - ) - assertEquals( - Value.U, - suspendCancellableCoroutine { c -> - resource.resolve(URI.create("https://baidu.com").toResource(), c) - } - ) - assertEquals( - Value.UKN, - suspendCancellableCoroutine { c -> - resource.resolve( - object : Resource { - override fun data(): ByteArray { - return byteArrayOf() - } - }, - c - ) - } - ) - - } -} diff --git a/simbot-api/src/jvmTest/kotlin/resource/SourceResourceTypeTests.kt b/simbot-api/src/jvmTest/kotlin/resource/SourceResourceTypeTests.kt index 0a074f92d..9c9cbed0a 100644 --- a/simbot-api/src/jvmTest/kotlin/resource/SourceResourceTypeTests.kt +++ b/simbot-api/src/jvmTest/kotlin/resource/SourceResourceTypeTests.kt @@ -23,7 +23,6 @@ package resource -import love.forte.simbot.resource.ExperimentalIOResourceAPI import love.forte.simbot.resource.SourceResource import love.forte.simbot.resource.toResource import java.io.File @@ -37,7 +36,6 @@ import kotlin.test.assertIs * * @author ForteScarlet */ -@OptIn(ExperimentalIOResourceAPI::class) class SourceResourceTypeTests { @Test