Skip to content

Commit

Permalink
Merge pull request #972 from simple-robot/v4-dev
Browse files Browse the repository at this point in the history
Release: v4.8.0
  • Loading branch information
ForteScarlet authored Nov 22, 2024
2 parents 17e0891 + 34115e4 commit fd2590d
Show file tree
Hide file tree
Showing 13 changed files with 144 additions and 54 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import io.gitlab.arturbosch.detekt.Detekt
plugins {
idea
id("simbot.dokka-multi-module")
id("com.github.gmazzo.buildconfig") version "5.5.0" apply false
id("com.github.gmazzo.buildconfig") version "5.5.1" apply false
alias(libs.plugins.detekt)
id("simbot.nexus-publish")
id("simbot.changelog-generator")
Expand Down
4 changes: 2 additions & 2 deletions buildSrc/src/main/kotlin/P.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ fun isSnapshot(): Boolean = _isSnapshot
@Suppress("MemberVisibilityCanBePrivate")
sealed class P(override val group: String) : ProjectDetail() {
companion object {
const val VERSION = "4.7.0"
const val NEXT_VERSION = "4.7.1"
const val VERSION = "4.8.0"
const val NEXT_VERSION = "4.8.1"
const val SNAPSHOT_VERSION = "$VERSION-SNAPSHOT"
const val NEXT_SNAPSHOT_VERSION = "$NEXT_VERSION-SNAPSHOT"

Expand Down
8 changes: 4 additions & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kotlin = "2.0.20"
dokka = "1.9.20"
kotlinx-coroutines = "1.9.0"
kotlinx-serialization = "1.7.3"
kotlinx-io = "0.5.4"
kotlinx-io = "0.6.0"
spring-boot-v2 = "2.7.18"
spring-boot-v3 = "3.2.1"
openjdk-jmh = "1.36"
Expand All @@ -13,12 +13,12 @@ slf4j = "2.0.16"
# https://github.com/google/ksp
ksp = "2.0.20-1.0.25"
# https://square.github.io/kotlinpoet/
kotlinPoet = "1.18.1"
kotlinPoet = "2.0.0"
# https://github.com/Kotlin/kotlinx-benchmark
#kotlinxBenchmark = "0.4.11"
reactor = "3.6.11"
reactor = "3.7.0"
# simbots
suspendTransform = "2.0.20-0.9.3"
suspendTransform = "2.0.20-0.9.4"
gradleCommon = "0.6.0"
# tests
mockk = "1.13.13"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ 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.*
Expand Down Expand Up @@ -443,6 +441,12 @@ public data class OfflineByteArrayImage(private val data: ByteArray) : OfflineIm
* 远程图片通常是通过事件推送、主动上传等手段得到的、有与某个远程服务器互相对应的唯一标识的图片。
* 这个标识可能是一个ID,或一个访问链接。
*
* 对于一个远程图片的数据获取方式(比如它的二进制数据流),
* 通常取决于其对应的来源(比如bot或事件)而不是 [RemoteImage] 类型自身。
*
* 例如一个仅包含ID的远程图片,需要拥有对应bot的认证信息才可以获取连接,
* 那么就无法仅通过此图片对象本身来获取下载连接。
*
* @see RemoteIDImage
*/
public interface RemoteImage : Image, IDAwareImage {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,48 +66,53 @@ public annotation class ExperimentalIOResourceAPI
* 如果不确定文件系统使用的路径分隔符,或可能在多个使用不同路径分隔符的系统上使用,
* 则考虑使用 [fileResource(base, ...parts)][fileResource]。
*
* 对文件存在性的校验和错误报告可能不会立即报告,而是被推迟到真正读取数据时,
* 参考 [SourceResource.source] 的可能异常。
*
* @param filePath 文件路径,是使用 _路径分隔符_ 的多个片段。
* 其中, _路径分隔符_ 在不同的文件系统中可能是不同的,例如在 Unit 中的 `/`
* 和在 Windows 的 `\`。
*
* @throws kotlinx.io.files.FileNotFoundException see [kotlinx.io.files.FileSystem.source].
* @throws kotlinx.io.IOException see [kotlinx.io.files.FileSystem.source].
* @see SourceResource
*
* @since 4.7.0
*/
@JvmName("valueOfPath")
@ExperimentalIOResourceAPI
@Throws(Exception::class)
public fun fileResource(filePath: String): Resource {
val path = Path(filePath)
return FilePathResource(path)
public fun fileResource(filePath: String): SourceResource {
return Path(filePath).toResource()
}

/**
* 根据文件路径片段集得到一个基于对应文件的 [Resource]。
*
* 文件会先在初始化时构造 [RawSource], 而后在读取 [Resource.data]
* 时使用 [Source]. 因此对文件存在性的校验和错误报告可能不会立即报告,
* 而是被推迟到真正读取数据时。
*
* 文件会在通过 [Resource.data] 读取数据时才会校验存在性。届时如果文件不存在,
* 则会得到 [IllegalStateException] 异常。
* 此异常的 [IllegalStateException.cause] 可能是:
* - [kotlinx.io.files.FileNotFoundException]
* - [kotlinx.io.IOException]
* 如果是这两个类型,则成因参考 [kotlinx.io.files.FileSystem.source]。
* 对文件存在性的校验和错误报告可能不会立即报告,而是被推迟到真正读取数据时,
* 参考 [SourceResource.source] 的可能异常。
*
* @throws kotlinx.io.files.FileNotFoundException see [kotlinx.io.files.FileSystem.source].
* @throws kotlinx.io.IOException see [kotlinx.io.files.FileSystem.source].
* @see SourceResource
*
* @since 4.7.0
*/
@JvmName("valueOfPath")
@ExperimentalIOResourceAPI
@Throws(Exception::class)
public fun fileResource(base: String, vararg parts: String): Resource {
val path = Path(base, *parts)
return FilePathResource(path)
public fun fileResource(base: String, vararg parts: String): SourceResource {
return Path(base, *parts).toResource()
}

/**
* 使用 [Path] 直接构建一个 [FilePathResource]
*
* 对文件存在性的校验和错误报告可能不会立即报告,而是被推迟到真正读取数据时,
* 参考 [SourceResource.source] 的可能异常。
*
* @see SourceResource
*
* @since 4.8.0
*/
@JvmName("valueOf")
@ExperimentalIOResourceAPI
public fun Path.toResource(): SourceResource {
return FilePathResource(this)
}

/**
Expand All @@ -121,6 +126,9 @@ public fun fileResource(base: String, vararg parts: String): Resource {
public interface SourceResource : Resource {
/**
* 得到一个用于本次数据读取的 [Source].
*
* 每次都将构建一个新的 [Source], 你需要自行按需[关闭][Source.close]它们。
*
* @throws kotlinx.io.files.FileNotFoundException
* see [kotlinx.io.files.FileSystem.source], [RawSource.buffered]
* @throws kotlinx.io.IOException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,14 @@ import kotlin.jvm.JvmName
*
* [Resource] 主要由内部实现,不保证对第三方实现的稳定与兼容
*
* @see ByteArrayResource
* @see SourceResource
*
* @author ForteScarlet
*/
public interface Resource {
// TODO become `sealed` for ByteArrayResource and SourceResource.

/**
* 读取此资源的字节数据。
*
Expand Down
14 changes: 5 additions & 9 deletions simbot-api/src/jvmMain/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@
*
*/

import love.forte.simbot.component.ComponentFactoryConfigurerProvider;
import love.forte.simbot.component.ComponentFactoryProvider;
import love.forte.simbot.plugin.PluginFactoryConfigurerProvider;
import love.forte.simbot.plugin.PluginFactoryProvider;

module simbot.api {
requires kotlin.stdlib;
requires simbot.logger;
Expand All @@ -38,6 +33,7 @@
requires kotlinx.coroutines.core;
requires kotlinx.serialization.core;
requires kotlinx.serialization.json;
requires kotlinx.io.core;
requires static kotlinx.coroutines.reactive;
requires static kotlinx.coroutines.reactor;
requires static kotlinx.coroutines.rx2;
Expand All @@ -60,9 +56,9 @@
exports love.forte.simbot.plugin;
exports love.forte.simbot.resource;

uses ComponentFactoryProvider;
uses ComponentFactoryConfigurerProvider;
uses PluginFactoryProvider;
uses PluginFactoryConfigurerProvider;
uses love.forte.simbot.component.ComponentFactoryProvider;
uses love.forte.simbot.component.ComponentFactoryConfigurerProvider;
uses love.forte.simbot.plugin.PluginFactoryProvider;
uses love.forte.simbot.plugin.PluginFactoryConfigurerProvider;

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@

package love.forte.simbot.resource

import kotlinx.io.Source
import kotlinx.io.asSource
import kotlinx.io.buffered
import love.forte.simbot.resource.JvmStringReadableResource.Companion.DEFAULT_CHARSET
import java.io.*
import java.net.MalformedURLException
import java.net.URI
Expand All @@ -34,6 +38,7 @@ 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
Expand All @@ -43,8 +48,8 @@ import kotlin.io.path.reader
*
* @author forte
*/
@OptIn(ExperimentalIOResourceAPI::class)
public interface InputStreamResource : Resource {

/**
* 读取当前资源的所有字节数据。
* 默认通过 [inputStream] 读取。
Expand Down Expand Up @@ -106,7 +111,7 @@ public interface ReaderResource : JvmStringReadableResource {
* 默认使用 [JvmStringReadableResource.DEFAULT_CHARSET] 编码。
*/
@Throws(IOException::class)
public fun reader(): Reader = reader(JvmStringReadableResource.DEFAULT_CHARSET)
public fun reader(): Reader = reader(DEFAULT_CHARSET)

/**
* 获取可用于读取当前资源数据的读取流。
Expand Down Expand Up @@ -140,7 +145,7 @@ public interface FileResource : InputStreamResource, ReaderResource {
* @throws FileNotFoundException 如果文件不存在
*/
@Throws(FileNotFoundException::class)
override fun reader(): Reader = reader(JvmStringReadableResource.DEFAULT_CHARSET)
override fun reader(): Reader = reader(DEFAULT_CHARSET)

/**
* 从与此资源关联的 [File] 创建新的 [Reader]
Expand All @@ -162,7 +167,7 @@ public interface FileResource : InputStreamResource, ReaderResource {
* @throws IOException 如果文件无法读取
*/
@Throws(IOException::class)
override fun string(): String = string(JvmStringReadableResource.DEFAULT_CHARSET)
override fun string(): String = string(DEFAULT_CHARSET)

/**
* 将与此资源关联的 [File] 读取为字符串
Expand All @@ -181,15 +186,22 @@ public interface FileResource : InputStreamResource, ReaderResource {
*/
@JvmName("valueOf")
@JvmOverloads
public fun File.toResource(charset: Charset = JvmStringReadableResource.DEFAULT_CHARSET): FileResource =
public fun File.toResource(charset: Charset = DEFAULT_CHARSET): FileResource =
FileResourceImpl(this, charset)

private data class FileResourceImpl(override val file: File, private val charset: Charset) : FileResource {
@OptIn(ExperimentalIOResourceAPI::class)
private data class FileResourceImpl(override val file: File, private val charset: Charset) :
FileResource, SourceResource {
override fun string(): String = string(charset)
override fun reader(): Reader = reader(charset)

override fun string(charset: Charset): String = file.readText(charset)
override fun reader(charset: Charset): Reader = file.reader(charset)

override fun data(): ByteArray = file.readBytes()

override fun source(): Source = inputStream().asSource().buffered()

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is FileResourceImpl) return false
Expand Down Expand Up @@ -261,16 +273,17 @@ public interface PathResource : InputStreamResource, ReaderResource {
@JvmName("valueOf")
@JvmOverloads
public fun Path.toResource(
charset: Charset = JvmStringReadableResource.DEFAULT_CHARSET,
charset: Charset = DEFAULT_CHARSET,
vararg options: OpenOption
): PathResource =
PathResourceImpl(this, charset, options)

@OptIn(ExperimentalIOResourceAPI::class)
private data class PathResourceImpl(
override val path: Path,
private val charset: Charset,
private val openOptions: Array<out OpenOption>
) : PathResource {
) : PathResource, SourceResource {
override fun inputStream(): InputStream = path.inputStream(options = openOptions)

override fun reader(): Reader = reader(charset)
Expand All @@ -279,6 +292,10 @@ private data class PathResourceImpl(
override fun reader(charset: Charset): Reader = path.reader(charset, options = openOptions)
override fun string(charset: Charset): String = reader(charset).use(Reader::readText)

override fun data(): ByteArray = path.readBytes()

override fun source(): Source = inputStream().asSource().buffered()

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is PathResourceImpl) return false
Expand Down Expand Up @@ -369,7 +386,7 @@ public interface URIResource : InputStreamResource, JvmStringReadableResource {
@kotlin.jvm.Throws(URISyntaxException::class)
@JvmName("valueOf")
@JvmOverloads
public fun URL.toResource(charset: Charset = JvmStringReadableResource.DEFAULT_CHARSET): URIResource =
public fun URL.toResource(charset: Charset = DEFAULT_CHARSET): URIResource =
URIResourceImpl(toURI(), charset, this)

/**
Expand All @@ -380,10 +397,13 @@ public fun URL.toResource(charset: Charset = JvmStringReadableResource.DEFAULT_C
*/
@JvmName("valueOf")
@JvmOverloads
public fun URI.toResource(charset: Charset = JvmStringReadableResource.DEFAULT_CHARSET): URIResource =
public fun URI.toResource(charset: Charset = DEFAULT_CHARSET): URIResource =
URIResourceImpl(this, charset, null)

private class URIResourceImpl(override val uri: URI, val charset: Charset, private var url: URL? = null) : URIResource {
@OptIn(ExperimentalIOResourceAPI::class)
private class URIResourceImpl(override val uri: URI, val charset: Charset, private var url: URL? = null) :
URIResource,
SourceResource {
private val urlValue: URL
get() = url ?: run {
uri.toURL().also { url = it }
Expand All @@ -393,5 +413,9 @@ private class URIResourceImpl(override val uri: URI, val charset: Charset, priva
override fun string(): String = string(charset)
override fun string(charset: Charset): String = urlValue.readText(charset)

override fun data(): ByteArray = inputStream().use { stream -> stream.readAllBytes() }

override fun source(): Source = inputStream().asSource().buffered()

override fun toString(): String = "URIResource(uri=$uri, charset=$charset)"
}
50 changes: 50 additions & 0 deletions simbot-api/src/jvmTest/kotlin/resource/SourceResourceTypeTests.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
*/

package resource

import love.forte.simbot.resource.ExperimentalIOResourceAPI
import love.forte.simbot.resource.SourceResource
import love.forte.simbot.resource.toResource
import java.io.File
import java.net.URI
import kotlin.io.path.Path
import kotlin.test.Test
import kotlin.test.assertIs


/**
*
* @author ForteScarlet
*/
@OptIn(ExperimentalIOResourceAPI::class)
class SourceResourceTypeTests {

@Test
fun testSourceResourceInheritance() {
assertIs<SourceResource>(File(".").toResource())
assertIs<SourceResource>(Path(".").toResource())
assertIs<SourceResource>(URI.create("https://localhost:8080").toResource())
}

}
Loading

0 comments on commit fd2590d

Please sign in to comment.