Skip to content

Commit dc96be2

Browse files
committed
feat(api): 增加部分基于文件系统的 Resource、Image API支持
1 parent 3048ad1 commit dc96be2

File tree

6 files changed

+180
-4
lines changed

6 files changed

+180
-4
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ apiValidation {
159159
listOf(
160160
"love.forte.simbot.annotations.ExperimentalSimbotAPI",
161161
"love.forte.simbot.annotations.InternalSimbotAPI",
162+
"love.forte.simbot.resource.ExperimentalIOResourceAPI",
162163
),
163164
)
164165

buildSrc/src/main/kotlin/P.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ fun isSnapshot(): Boolean = _isSnapshot
4848
sealed class P(override val group: String) : ProjectDetail() {
4949
companion object {
5050
const val VERSION = "4.6.1"
51-
const val NEXT_VERSION = "4.6.2"
51+
const val NEXT_VERSION = "4.7.0"
5252
const val SNAPSHOT_VERSION = "$VERSION-SNAPSHOT"
5353
const val NEXT_SNAPSHOT_VERSION = "$NEXT_VERSION-SNAPSHOT"
5454

simbot-api/api/simbot-api.api

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2532,6 +2532,9 @@ public abstract interface class love/forte/simbot/resource/ByteArrayResource : l
25322532
public abstract fun data ()[B
25332533
}
25342534

2535+
public abstract interface annotation class love/forte/simbot/resource/ExperimentalIOResourceAPI : java/lang/annotation/Annotation {
2536+
}
2537+
25352538
public abstract interface class love/forte/simbot/resource/FileResource : love/forte/simbot/resource/InputStreamResource, love/forte/simbot/resource/ReaderResource {
25362539
public fun data ()[B
25372540
public abstract fun getFile ()Ljava/io/File;

simbot-api/src/commonMain/kotlin/love/forte/simbot/message/StandardMessages.kt

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ import love.forte.simbot.message.At.Companion.equals
3434
import love.forte.simbot.message.At.Companion.hashCode
3535
import love.forte.simbot.message.OfflineImage.Companion.toOfflineImage
3636
import love.forte.simbot.message.Text.Companion.of
37-
import love.forte.simbot.resource.ByteArrayResource
38-
import love.forte.simbot.resource.Resource
39-
import love.forte.simbot.resource.ResourceBase64Serializer
37+
import love.forte.simbot.resource.*
4038
import kotlin.io.encoding.ExperimentalEncodingApi
4139
import kotlin.js.JsName
4240
import kotlin.jvm.*
@@ -340,6 +338,37 @@ public interface OfflineImage : Image {
340338
else -> toOfflineResourceImage()
341339
}
342340

341+
/**
342+
* 使用文件路径读取文件 `Path` 作为 [OfflineImage]。
343+
* 相当于通过 [fileResource] 产物使用 [toOfflineImage]。
344+
*
345+
* 更多说明和注意事项参考 [fileResource]。
346+
*
347+
* @see fileResource
348+
*
349+
* @since 4.7.0
350+
*/
351+
@JvmStatic
352+
@JvmName("ofFilePath")
353+
@ExperimentalIOResourceAPI
354+
public fun fileOfflineImage(filePath: String): OfflineImage =
355+
fileResource(filePath).toOfflineResourceImage()
356+
357+
/**
358+
* 使用文件路径读取文件 `Path` 作为 [OfflineImage]。
359+
* 相当于通过 [fileResource] 产物使用 [toOfflineImage]。
360+
*
361+
* 更多说明和注意事项参考 [fileResource]。
362+
*
363+
* @see fileResource
364+
*
365+
* @since 4.7.0
366+
*/
367+
@JvmStatic
368+
@ExperimentalIOResourceAPI
369+
@JvmName("ofFilePath")
370+
public fun fileOfflineImage(base: String, vararg parts: String): OfflineImage =
371+
fileResource(base, *parts).toOfflineResourceImage()
343372
}
344373
}
345374

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright (c) 2024. ForteScarlet.
3+
*
4+
* Project https://github.com/simple-robot/simpler-robot
5+
6+
*
7+
* This file is part of the Simple Robot Library (Alias: simple-robot, simbot, etc.).
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Lesser General Public License as published by
11+
* the Free Software Foundation, either version 3 of the License, or
12+
* (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* Lesser GNU General Public License for more details.
18+
*
19+
* You should have received a copy of the Lesser GNU General Public License
20+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
21+
*
22+
*/
23+
@file:JvmName("Resources")
24+
@file:JvmMultifileClass
25+
26+
package love.forte.simbot.resource
27+
28+
import kotlinx.io.*
29+
import kotlinx.io.files.FileNotFoundException
30+
import kotlinx.io.files.Path
31+
import kotlinx.io.files.SystemFileSystem
32+
import kotlin.annotation.AnnotationTarget.*
33+
import kotlin.jvm.JvmMultifileClass
34+
import kotlin.jvm.JvmName
35+
36+
/**
37+
* 一些尚处于实验阶段的、基于IO(主要指文件系统相关)的 [Resource] 相关API。
38+
*
39+
* 可能会在未来发生变更、或被删除,且不保证兼容性与稳定性。
40+
*
41+
* @since 4.7.0
42+
*/
43+
@RequiresOptIn("Experimental IO Resource API")
44+
@Retention(AnnotationRetention.BINARY)
45+
@MustBeDocumented
46+
@Target(
47+
CLASS,
48+
ANNOTATION_CLASS,
49+
PROPERTY,
50+
FIELD,
51+
LOCAL_VARIABLE,
52+
VALUE_PARAMETER,
53+
CONSTRUCTOR,
54+
FUNCTION,
55+
PROPERTY_GETTER,
56+
PROPERTY_SETTER,
57+
TYPEALIAS
58+
)
59+
public annotation class ExperimentalIOResourceAPI
60+
61+
/**
62+
* 根据完整的文件路径 [filePath] 得到一个基于对应文件的 [Resource]。
63+
*
64+
* 文件会在通过 [Resource.data] 读取数据时才会校验存在性。届时如果文件不存在,
65+
* 则会得到 [IllegalStateException] 异常。
66+
*
67+
* 如果不确定文件系统使用的路径分隔符,或可能在多个使用不同路径分隔符的系统上使用,
68+
* 则考虑使用 [fileResource(base, ...parts)][fileResource]。
69+
*
70+
* @param filePath 文件路径,是使用 _路径分隔符_ 的多个片段。
71+
* 其中, _路径分隔符_ 在不同的文件系统中可能是不同的,例如在 Unit 中的 `/`
72+
* 和在 Windows 的 `\`。
73+
*
74+
* @since 4.7.0
75+
*/
76+
@JvmName("valueOfPath")
77+
@ExperimentalIOResourceAPI
78+
public fun fileResource(filePath: String): Resource {
79+
val path = Path(filePath)
80+
return FilePathResource(path)
81+
}
82+
83+
/**
84+
* 根据文件路径片段集得到一个基于对应文件的 [Resource]。
85+
*
86+
* 文件会在通过 [Resource.data] 读取数据时才会校验存在性。届时如果文件不存在,
87+
* 则会得到 [IllegalStateException] 异常。
88+
* 此异常的 [IllegalStateException.cause] 可能是:
89+
* - [kotlinx.io.files.FileNotFoundException]
90+
* - [kotlinx.io.IOException]
91+
* 如果是这两个类型,则成因参考 [kotlinx.io.files.FileSystem.source]。
92+
*
93+
* @since 4.7.0
94+
*/
95+
@JvmName("valueOfPath")
96+
@ExperimentalIOResourceAPI
97+
public fun fileResource(base: String, vararg parts: String): Resource {
98+
val path = Path(base, *parts)
99+
return FilePathResource(path)
100+
}
101+
102+
/**
103+
* 一个可以得到 [kotlinx.io.RawSource] 的 [Resource]。
104+
*
105+
* @since 4.7.0
106+
*/
107+
@ExperimentalIOResourceAPI
108+
public interface RawSourceResource : Resource {
109+
public fun source(): RawSource
110+
111+
override fun data(): ByteArray {
112+
return source().buffered().use { it.readByteArray() }
113+
}
114+
}
115+
116+
/**
117+
* 一个可以得到 [kotlinx.io.Source] 的 [Resource]。
118+
*
119+
* @since 4.7.0
120+
*/
121+
@ExperimentalIOResourceAPI
122+
public interface SourceResource : RawSourceResource {
123+
override fun source(): Source
124+
}
125+
126+
@ExperimentalIOResourceAPI
127+
private data class FilePathResource(val path: Path) : RawSourceResource {
128+
override fun source(): RawSource = try {
129+
SystemFileSystem.source(path)
130+
} catch (fnf: FileNotFoundException) {
131+
throw IllegalStateException(fnf.message, fnf)
132+
} catch (io: IOException) {
133+
throw IllegalStateException(io.message, io)
134+
}
135+
}
136+

simbot-api/src/commonMain/kotlin/love/forte/simbot/resource/Resource.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ public interface ByteArrayResource : Resource {
8787

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

0 commit comments

Comments
 (0)