blob: 26e568431838b0cde6f226609d4967579b5d9226 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
package at.hannibal2.skyhanni.utils.const
@JvmInline
/**
* Immutable view of an object.
* This class wraps an [T] indicating that it should not be modified. This allows multiple users to share an
* object instance without having to fear that the internal mutability of the object causes unexpected behaviour.
* More specifically, as long as the invariants of the methods and constructors are followed by all users then data
* contained within will never change.
*
* For specific [T]s there are extension methods allowing to safely access the data.
*
* This is a [JvmInline] class, so at
* runtime [Const] is a 0 cost wrapper.
*/
value class Const<T> private constructor(
/**
* Unsafely access the underlying object. Callers of this method promise not to modify the returned instance, or to
* leak this instance or references/views into this instance to any other codepaths which modify it.
* Whenever possible callers should wrap objects they return which offer a view into this object into a [Const] of
* its own.
*/
@PublishedApi
internal val unsafeMutable: T,
) {
companion object {
/**
* Create a new [Const] instance. Callers of this method guarantee that the given object will not be mutated
* internally. This should ideally be done by every instance of [value] being wrapped in a [Const] (and other
* references to be discarded as quickly as possible).
*/
fun <T> newUnchecked(value: T): Const<T> {
return Const(value)
}
}
/**
* Map the contained object into a new [Const].
*/
inline fun <U> unsafeMap(
/**
* This lambda needs to hold the same guarantees for its parameter like for access to [unsafeMutable].
* It can however assume that the return value will never be modified by other code.
*/
mapper: (T) -> U
): Const<U> {
return unsafeMutable
.let(mapper)
.let(::newUnchecked)
}
/**
* Flat map the contained object into a new [Const]. Behaves like [unsafeMap] but allowing the [mapper] to return a [Const]
*/
inline fun <U> unsafeFlatMap(
/**
* This lambda needs to hold the same guarantees for its parameter like for access to [unsafeMutable].
*/
mapper: (T) -> Const<U>
): Const<U> {
return unsafeMutable
.let(mapper)
}
}
/**
* Flatten two nested [Const] into just one.
*/
fun <T> Const<Const<T>>.flatten(): Const<T> = unsafeFlatMap { it }
/**
* Lift nullability out of a [Const] to allow for easier `?.` operations.
*/
fun <T : Any> Const<T?>.liftNull(): Const<T>? = unsafeMutable?.let(Const.Companion::newUnchecked)
inline fun <reified U : T, T> Const<T>.tryCast(): Const<U>? = (unsafeMutable as? U)?.let(Const.Companion::newUnchecked)
/**
* List a list out of a const. This is legal since [List] does not allow for mutating unless it's elements are individually
* mutable as long. The caller may never cast the returned instance to [MutableList].
*/
fun <T> Const<List<T>>.liftList(): List<Const<T>> {
return unsafeMutable.map(Const.Companion::newUnchecked)
}
/**
* Lift const out of a [List]. The caller must guarantee that the list instance is never modified. This means it was
* either constructed directly as a [List] or if it originally comes from a [MutableList], mutations operations on this
* instance are never used.
*/
fun <T> List<Const<T>>.liftConst(): Const<List<T>> {
return Const.newUnchecked(this.map { it.unsafeMutable })
}
|