Skip to content

Commit

Permalink
feat(api): 增加部分基于文件系统的 Resource、Image API支持
Browse files Browse the repository at this point in the history
  • Loading branch information
ForteScarlet committed Oct 14, 2024
1 parent 3048ad1 commit dc96be2
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 4 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ apiValidation {
listOf(
"love.forte.simbot.annotations.ExperimentalSimbotAPI",
"love.forte.simbot.annotations.InternalSimbotAPI",
"love.forte.simbot.resource.ExperimentalIOResourceAPI",
),
)

Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/P.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fun isSnapshot(): Boolean = _isSnapshot
sealed class P(override val group: String) : ProjectDetail() {
companion object {
const val VERSION = "4.6.1"
const val NEXT_VERSION = "4.6.2"
const val NEXT_VERSION = "4.7.0"
const val SNAPSHOT_VERSION = "$VERSION-SNAPSHOT"
const val NEXT_SNAPSHOT_VERSION = "$NEXT_VERSION-SNAPSHOT"

Expand Down
3 changes: 3 additions & 0 deletions simbot-api/api/simbot-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -2532,6 +2532,9 @@ public abstract interface class love/forte/simbot/resource/ByteArrayResource : l
public abstract fun data ()[B
}

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 fun data ()[B
public abstract fun getFile ()Ljava/io/File;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ 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.ByteArrayResource
import love.forte.simbot.resource.Resource
import love.forte.simbot.resource.ResourceBase64Serializer
import love.forte.simbot.resource.*
import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.js.JsName
import kotlin.jvm.*
Expand Down Expand Up @@ -340,6 +338,37 @@ public interface OfflineImage : Image {
else -> toOfflineResourceImage()
}

/**
* 使用文件路径读取文件 `Path` 作为 [OfflineImage]。
* 相当于通过 [fileResource] 产物使用 [toOfflineImage]。
*
* 更多说明和注意事项参考 [fileResource]。
*
* @see fileResource
*
* @since 4.7.0
*/
@JvmStatic
@JvmName("ofFilePath")
@ExperimentalIOResourceAPI
public fun fileOfflineImage(filePath: String): OfflineImage =
fileResource(filePath).toOfflineResourceImage()

/**
* 使用文件路径读取文件 `Path` 作为 [OfflineImage]。
* 相当于通过 [fileResource] 产物使用 [toOfflineImage]。
*
* 更多说明和注意事项参考 [fileResource]。
*
* @see fileResource
*
* @since 4.7.0
*/
@JvmStatic
@ExperimentalIOResourceAPI
@JvmName("ofFilePath")
public fun fileOfflineImage(base: String, vararg parts: String): OfflineImage =
fileResource(base, *parts).toOfflineResourceImage()
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2024. ForteScarlet.
*
* Project https://github.com/simple-robot/simpler-robot
* Email [email protected]
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
@file:JvmName("Resources")
@file:JvmMultifileClass

package love.forte.simbot.resource

import kotlinx.io.*
import kotlinx.io.files.FileNotFoundException
import kotlinx.io.files.Path
import kotlinx.io.files.SystemFileSystem
import kotlin.annotation.AnnotationTarget.*
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName

/**
* 一些尚处于实验阶段的、基于IO(主要指文件系统相关)的 [Resource] 相关API。
*
* 可能会在未来发生变更、或被删除,且不保证兼容性与稳定性。
*
* @since 4.7.0
*/
@RequiresOptIn("Experimental IO Resource API")
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@Target(
CLASS,
ANNOTATION_CLASS,
PROPERTY,
FIELD,
LOCAL_VARIABLE,
VALUE_PARAMETER,
CONSTRUCTOR,
FUNCTION,
PROPERTY_GETTER,
PROPERTY_SETTER,
TYPEALIAS
)
public annotation class ExperimentalIOResourceAPI

/**
* 根据完整的文件路径 [filePath] 得到一个基于对应文件的 [Resource]。
*
* 文件会在通过 [Resource.data] 读取数据时才会校验存在性。届时如果文件不存在,
* 则会得到 [IllegalStateException] 异常。
*
* 如果不确定文件系统使用的路径分隔符,或可能在多个使用不同路径分隔符的系统上使用,
* 则考虑使用 [fileResource(base, ...parts)][fileResource]。
*
* @param filePath 文件路径,是使用 _路径分隔符_ 的多个片段。
* 其中, _路径分隔符_ 在不同的文件系统中可能是不同的,例如在 Unit 中的 `/`
* 和在 Windows 的 `\`。
*
* @since 4.7.0
*/
@JvmName("valueOfPath")
@ExperimentalIOResourceAPI
public fun fileResource(filePath: String): Resource {
val path = Path(filePath)
return FilePathResource(path)
}

/**
* 根据文件路径片段集得到一个基于对应文件的 [Resource]。
*
* 文件会在通过 [Resource.data] 读取数据时才会校验存在性。届时如果文件不存在,
* 则会得到 [IllegalStateException] 异常。
* 此异常的 [IllegalStateException.cause] 可能是:
* - [kotlinx.io.files.FileNotFoundException]
* - [kotlinx.io.IOException]
* 如果是这两个类型,则成因参考 [kotlinx.io.files.FileSystem.source]。
*
* @since 4.7.0
*/
@JvmName("valueOfPath")
@ExperimentalIOResourceAPI
public fun fileResource(base: String, vararg parts: String): Resource {
val path = Path(base, *parts)
return FilePathResource(path)
}

/**
* 一个可以得到 [kotlinx.io.RawSource] 的 [Resource]。
*
* @since 4.7.0
*/
@ExperimentalIOResourceAPI
public interface RawSourceResource : Resource {
public fun source(): RawSource

override fun data(): ByteArray {
return source().buffered().use { it.readByteArray() }
}
}

/**
* 一个可以得到 [kotlinx.io.Source] 的 [Resource]。
*
* @since 4.7.0
*/
@ExperimentalIOResourceAPI
public interface SourceResource : RawSourceResource {
override fun source(): Source
}

@ExperimentalIOResourceAPI
private data class FilePathResource(val path: Path) : RawSourceResource {
override fun source(): RawSource = try {
SystemFileSystem.source(path)
} catch (fnf: FileNotFoundException) {
throw IllegalStateException(fnf.message, fnf)
} catch (io: IOException) {
throw IllegalStateException(io.message, io)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ public interface ByteArrayResource : Resource {

/**
* 基于 [Base64] 的 [Resource] 序列化器。
*
* 它会将任何 [Resource] 都根据 [Resource.data] 序列化为 Base64 数据,
* 并将任意序列化后的数据反序列化为 [ByteArrayResource]。
*
* 也因此,这会导致:
* - 序列化时会读取数据、产生读取开销。
* - 反序列化后的类型可能与原本的类型不一致。
*/
@ExperimentalEncodingApi
public object ResourceBase64Serializer : KSerializer<Resource> {
Expand Down

0 comments on commit dc96be2

Please sign in to comment.