Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce auto close for ExternalResource #1392

Merged
merged 2 commits into from
Sep 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5584,12 +5584,14 @@ public abstract interface class net/mamoe/mirai/utils/ExternalResource : java/io
public static fun create (Ljava/io/RandomAccessFile;Ljava/lang/String;Z)Lnet/mamoe/mirai/utils/ExternalResource;
public static fun create ([B)Lnet/mamoe/mirai/utils/ExternalResource;
public static fun create ([BLjava/lang/String;)Lnet/mamoe/mirai/utils/ExternalResource;
public static fun createAutoCloseable (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/utils/ExternalResource;
public abstract fun getClosed ()Lkotlinx/coroutines/Deferred;
public abstract fun getFormatName ()Ljava/lang/String;
public abstract fun getMd5 ()[B
public fun getSha1 ()[B
public abstract fun getSize ()J
public abstract fun inputStream ()Ljava/io/InputStream;
public fun isAutoClose ()Z
public static fun sendAsFile (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt;
public static fun sendAsFile (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static fun sendAsFile (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lnet/mamoe/mirai/utils/RemoteFile$ProgressionCallback;)Lnet/mamoe/mirai/message/MessageReceipt;
Expand Down Expand Up @@ -5645,6 +5647,7 @@ public final class net/mamoe/mirai/utils/ExternalResource$Companion {
public static synthetic fun create$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;Ljava/io/InputStream;Ljava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/utils/ExternalResource;
public static synthetic fun create$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;Ljava/io/RandomAccessFile;Ljava/lang/String;ZILjava/lang/Object;)Lnet/mamoe/mirai/utils/ExternalResource;
public static synthetic fun create$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;[BLjava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/utils/ExternalResource;
public final fun createAutoCloseable (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/utils/ExternalResource;
public final fun sendAsFile (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt;
public final fun sendAsFile (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun sendAsFile (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lnet/mamoe/mirai/utils/RemoteFile$ProgressionCallback;)Lnet/mamoe/mirai/message/MessageReceipt;
Expand Down Expand Up @@ -5701,6 +5704,11 @@ public final class net/mamoe/mirai/utils/ExternalResource$Companion {
public static synthetic fun uploadTo$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lnet/mamoe/mirai/utils/RemoteFile$ProgressionCallback;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public final class net/mamoe/mirai/utils/ExternalResourceKt {
public static final fun runAutoClose (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun useAutoClose (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
}

public abstract interface class net/mamoe/mirai/utils/FileCacheStrategy {
public static final field Companion Lnet/mamoe/mirai/utils/FileCacheStrategy$Companion;
public static fun getPlatformDefault ()Lnet/mamoe/mirai/utils/FileCacheStrategy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5584,12 +5584,14 @@ public abstract interface class net/mamoe/mirai/utils/ExternalResource : java/io
public static fun create (Ljava/io/RandomAccessFile;Ljava/lang/String;Z)Lnet/mamoe/mirai/utils/ExternalResource;
public static fun create ([B)Lnet/mamoe/mirai/utils/ExternalResource;
public static fun create ([BLjava/lang/String;)Lnet/mamoe/mirai/utils/ExternalResource;
public static fun createAutoCloseable (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/utils/ExternalResource;
public abstract fun getClosed ()Lkotlinx/coroutines/Deferred;
public abstract fun getFormatName ()Ljava/lang/String;
public abstract fun getMd5 ()[B
public fun getSha1 ()[B
public abstract fun getSize ()J
public abstract fun inputStream ()Ljava/io/InputStream;
public fun isAutoClose ()Z
public static fun sendAsFile (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt;
public static fun sendAsFile (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static fun sendAsFile (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lnet/mamoe/mirai/utils/RemoteFile$ProgressionCallback;)Lnet/mamoe/mirai/message/MessageReceipt;
Expand Down Expand Up @@ -5645,6 +5647,7 @@ public final class net/mamoe/mirai/utils/ExternalResource$Companion {
public static synthetic fun create$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;Ljava/io/InputStream;Ljava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/utils/ExternalResource;
public static synthetic fun create$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;Ljava/io/RandomAccessFile;Ljava/lang/String;ZILjava/lang/Object;)Lnet/mamoe/mirai/utils/ExternalResource;
public static synthetic fun create$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;[BLjava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/utils/ExternalResource;
public final fun createAutoCloseable (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/utils/ExternalResource;
public final fun sendAsFile (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt;
public final fun sendAsFile (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun sendAsFile (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lnet/mamoe/mirai/utils/RemoteFile$ProgressionCallback;)Lnet/mamoe/mirai/message/MessageReceipt;
Expand Down Expand Up @@ -5701,6 +5704,11 @@ public final class net/mamoe/mirai/utils/ExternalResource$Companion {
public static synthetic fun uploadTo$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lnet/mamoe/mirai/utils/RemoteFile$ProgressionCallback;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public final class net/mamoe/mirai/utils/ExternalResourceKt {
public static final fun runAutoClose (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun useAutoClose (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
}

public abstract interface class net/mamoe/mirai/utils/FileCacheStrategy {
public static final field Companion Lnet/mamoe/mirai/utils/FileCacheStrategy$Companion;
public static fun getPlatformDefault ()Lnet/mamoe/mirai/utils/FileCacheStrategy;
Expand Down
70 changes: 70 additions & 0 deletions mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage
import net.mamoe.mirai.utils.RemoteFile.Companion.sendFile
import net.mamoe.mirai.utils.RemoteFile.Companion.uploadFile
import java.io.*
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract


/**
Expand Down Expand Up @@ -67,6 +69,19 @@ import java.io.*
*/
public interface ExternalResource : Closeable {

/**
* 是否在 _使用一次_ 后自动 [close].
*
* 该属性仅供调用方参考. 如 [Contact.uploadImage] 会在方法结束时关闭 [isAutoClose] 为 `true` 的 [ExternalResource], 无论上传图片是否成功.
*
* 所有 mirai 内置的上传图片, 上传语音等方法都支持该行为.
*
* @since 2.8
*/
@MiraiExperimentalApi
public val isAutoClose: Boolean
get() = false

/**
* 文件内容 MD5. 16 bytes
*/
Expand Down Expand Up @@ -188,6 +203,23 @@ public interface ExternalResource : Closeable {
public fun InputStream.toExternalResource(formatName: String? = null): ExternalResource =
Mirai.FileCacheStrategy.newCache(this, formatName)

/**
* 创建一个在 _使用一次_ 后就会自动 [close] 的 [ExternalResource].
*
* @since 2.8
*/
@JvmName("createAutoCloseable")
@JvmStatic
public fun ExternalResource.toAutoCloseable(): ExternalResource {
return if (isAutoClose) this else {
val delegate = this
object : ExternalResource by delegate {
override val isAutoClose: Boolean get() = true
override fun toString(): String = "ExternalResourceWithAutoClose(delegate=$delegate)"
}
}
}

// endregion

///////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -427,3 +459,41 @@ public interface ExternalResource : Closeable {
// endregion
}
}

/**
* 执行 [action], 如果 [ExternalResource.isAutoClose], 在执行完成后调用 [ExternalResource.close].
*
* @since 2.8
*/
@MiraiExperimentalApi
// Continuing mark it as experimental until Kotlin's contextual receivers design is published.
// We might be able to make `action` a type `context(ExternalResource) () -> R`.
public inline fun <T : ExternalResource, R> T.withAutoClose(action: () -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
trySafely(
block = { return action() },
finally = { if (isAutoClose) close() }
)
}

/**
* 执行 [action], 如果 [ExternalResource.isAutoClose], 在执行完成后调用 [ExternalResource.close].
*
* @since 2.8
*/
@MiraiExperimentalApi
public inline fun <T : ExternalResource, R> T.runAutoClose(action: T.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return withAutoClose { action() }
}

/**
* 执行 [action], 如果 [ExternalResource.isAutoClose], 在执行完成后调用 [ExternalResource.close].
*
* @since 2.8
*/
@MiraiExperimentalApi
public inline fun <T : ExternalResource, R> T.useAutoClose(action: (resource: T) -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return runAutoClose(action)
}
30 changes: 30 additions & 0 deletions mirai-core-utils/src/commonMain/kotlin/StandardUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,36 @@ public inline fun Throwable.findCause(maxDepth: Int = 20, filter: (Throwable) ->
}
}

/**
* Run [block] and do [finally], catching exception thrown in [finally] and add it to the exception from [block].
*/
public inline fun <R> trySafely(
block: () -> R,
finally: () -> Unit,
): R {
// contract {
// callsInPlace(block, InvocationKind.EXACTLY_ONCE)
// callsInPlace(finally, InvocationKind.EXACTLY_ONCE)
// }
var eInBlock: Throwable? = null
try {
return block()
} catch (e: Throwable) {
eInBlock = e
} finally {
try {
finally()
} catch (eInFinally: Throwable) {
if (eInBlock != null) {
eInBlock.addSuppressed(eInFinally)
throw eInBlock
} else throw eInFinally
}
if (eInBlock != null) throw eInBlock
}
throw AssertionError()
}

public inline fun Throwable.findCauseOrSelf(maxDepth: Int = 20, filter: (Throwable) -> Boolean): Throwable =
findCause(maxDepth, filter) ?: this

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/

package net.mamoe.mirai.utils

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import java.io.IOException
import kotlin.test.assertEquals
import kotlin.test.assertIs

internal class TrySafelyTest {
@Test
fun `can run block`() {
assertEquals(
1,
trySafely(
block = { 1 },
finally = { }
)
)
}

@Test
fun `can run finally when no exception in block`() {
var x = 0
trySafely(
block = { },
finally = { x = 1 }
)
assertEquals(1, x)
}

@Test
fun `can run finally when exception in block`() {
var x = 0
assertThrows<Exception> {
trySafely(
block = { throw Exception() },
finally = { x = 1 }
)
}
assertEquals(1, x)
}

@Test
fun `can run finally catching`() {
assertThrows<NoSuchElementException> {
trySafely(
block = { throw NoSuchElementException() },
finally = { throw IOException() }
)
}.let { e ->
assertIs<IOException>(e.suppressed.single())
}
}
}
8 changes: 4 additions & 4 deletions mirai-core/src/commonMain/kotlin/contact/AbstractUser.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/

package net.mamoe.mirai.internal.contact
Expand Down Expand Up @@ -73,7 +73,7 @@ internal sealed class AbstractUser(
open val info: UserInfo = userInfo

@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
override suspend fun uploadImage(resource: ExternalResource): Image {
override suspend fun uploadImage(resource: ExternalResource): Image = resource.withAutoClose {
if (BeforeImageUploadEvent(this, resource).broadcast().isCancelled) {
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
}
Expand Down
16 changes: 7 additions & 9 deletions mirai-core/src/commonMain/kotlin/contact/FriendImpl.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/

@file:OptIn(LowLevelApi::class)
Expand All @@ -28,15 +28,13 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.PttStore
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.audioCodec
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.OfflineAudio
import net.mamoe.mirai.utils.ExternalResource
import net.mamoe.mirai.utils.recoverCatchingSuppressed
import net.mamoe.mirai.utils.toByteArray
import net.mamoe.mirai.utils.toUHexString
import net.mamoe.mirai.utils.*
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext
Expand Down Expand Up @@ -83,7 +81,7 @@ internal class FriendImpl(

override fun toString(): String = "Friend($id)"

override suspend fun uploadAudio(resource: ExternalResource): OfflineAudio = bot.network.run {
override suspend fun uploadAudio(resource: ExternalResource): OfflineAudio = resource.withAutoClose {
var audio: OfflineAudioImpl? = null
kotlin.runCatching {
val resp = Highway.uploadResourceBdh(
Expand Down Expand Up @@ -115,7 +113,7 @@ internal class FriendImpl(
)
)
}.recoverCatchingSuppressed {
when (val resp = PttStore.GroupPttUp(bot.client, bot.id, id, resource).sendAndExpect<Any>()) {
when (val resp = PttStore.GroupPttUp(bot.client, bot.id, id, resource).sendAndExpect(bot)) {
is PttStore.GroupPttUp.Response.RequireUpload -> {
tryServersUpload(
bot,
Expand Down
6 changes: 3 additions & 3 deletions mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ internal class GroupImpl constructor(
}

@OptIn(ExperimentalTime::class)
override suspend fun uploadImage(resource: ExternalResource): Image {
override suspend fun uploadImage(resource: ExternalResource): Image = resource.withAutoClose {
if (BeforeImageUploadEvent(this, resource).broadcast().isCancelled) {
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
}
Expand Down Expand Up @@ -217,7 +217,7 @@ internal class GroupImpl constructor(
}

@Suppress("OverridingDeprecatedMember", "DEPRECATION")
override suspend fun uploadVoice(resource: ExternalResource): Voice {
override suspend fun uploadVoice(resource: ExternalResource): Voice = resource.withAutoClose {
return bot.network.run {
uploadAudioResource(resource)

Expand Down Expand Up @@ -262,7 +262,7 @@ internal class GroupImpl constructor(
}.getOrThrow()
}

override suspend fun uploadAudio(resource: ExternalResource): OfflineAudio {
override suspend fun uploadAudio(resource: ExternalResource): OfflineAudio = resource.withAutoClose {
return bot.network.run {
uploadAudioResource(resource)

Expand Down
8 changes: 4 additions & 4 deletions mirai-core/src/commonMain/kotlin/utils/RemoteFileImpl.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/

package net.mamoe.mirai.internal.utils
Expand Down Expand Up @@ -440,7 +440,7 @@ internal class RemoteFileImpl(
private suspend fun upload0(
resource: ExternalResource,
callback: RemoteFile.ProgressionCallback?,
): Oidb0x6d6.UploadFileRspBody? {
): Oidb0x6d6.UploadFileRspBody? = resource.withAutoClose {
val parent = parent ?: return null
val parentInfo = parent.getFileFolderInfo() ?: return null
val resp = FileManagement.RequestUpload(
Expand Down