Skip to content

Commit a97ce12

Browse files
Merge pull request #14
* create possible error structure and builder from try without expected… * implement as possible error extension function for try without expect… * implement possible error builder with either with left as error and r… * implement possible error builder with either with right as error and … * implement as possible error extension function for either with left e… * implement as possible error extension function for either with right … * implement possible error builder with maybe of error * implement as possible error extension function for maybe error * implement to maybe error function for possible error * implement map function for possible error * implement test cases for equals and to string for possible error
1 parent 66506e6 commit a97ce12

2 files changed

Lines changed: 378 additions & 0 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.benromdhane.omar.offroadsoft.monad.error
2+
3+
import com.benromdhane.omar.offroadsoft.monad.Either
4+
import com.benromdhane.omar.offroadsoft.monad.Maybe
5+
import kotlin.jvm.JvmName
6+
7+
sealed interface PossibleError<ERROR : Any> {
8+
9+
fun error(): Boolean
10+
fun toMaybeError(): Maybe<ERROR>
11+
fun <NEW_ERROR : Any> map(mapper: (ERROR) -> NEW_ERROR): PossibleError<NEW_ERROR>
12+
13+
companion object Builder {
14+
15+
fun of(`try`: Try<Unit>) =
16+
`try`.toMaybeFailure()
17+
.map { Error.of<Throwable>(it) }
18+
.or(Success.of())
19+
20+
@JvmName("ofEitherLeftError")
21+
fun <ERROR : Any> of(either: Either<ERROR, Unit>) =
22+
either.toMaybeLeft()
23+
.map { Error.of<ERROR>(it) }
24+
.or(Success.of())
25+
26+
@JvmName("ofEitherRightError")
27+
fun <ERROR : Any> of(either: Either<Unit, ERROR>) =
28+
either.toMaybeRight()
29+
.map { Error.of<ERROR>(it) }
30+
.or(Success.of())
31+
32+
fun <ERROR : Any> of(maybe: Maybe<ERROR>) =
33+
maybe
34+
.map { Error.of<ERROR>(it) }
35+
.or(Success.of())
36+
}
37+
38+
@ConsistentCopyVisibility
39+
private data class Error<ERROR : Any> private constructor(
40+
val error: ERROR
41+
) : PossibleError<ERROR> {
42+
43+
override fun error() = true
44+
override fun toMaybeError() = Maybe.NotEmpty.of(this.error)
45+
override fun <NEW_ERROR : Any> map(mapper: (ERROR) -> NEW_ERROR) = Error(mapper(this.error))
46+
47+
companion object Builder {
48+
49+
fun <ERROR : Any> of(error: ERROR): PossibleError<ERROR> = Error(error)
50+
}
51+
}
52+
53+
private class Success<ERROR : Any> private constructor() : PossibleError<ERROR> {
54+
55+
override fun error() = false
56+
override fun toMaybeError() = Maybe.Empty.of<ERROR>()
57+
override fun <NEW_ERROR : Any> map(mapper: (ERROR) -> NEW_ERROR) = Success<NEW_ERROR>()
58+
59+
override fun toString() = "Success"
60+
override fun equals(other: Any?) =
61+
if (this === other) true
62+
else if (other == null || this::class != other::class) false
63+
else true
64+
65+
override fun hashCode() = this::class.hashCode()
66+
67+
companion object Builder {
68+
69+
fun <ERROR : Any> of(): PossibleError<ERROR> = Success()
70+
}
71+
}
72+
}
73+
74+
fun Try<Unit>.asPossibleError() = PossibleError.of(this)
75+
76+
@JvmName("eitherLeftErrorAsPossibleError")
77+
fun <ERROR : Any> Either<ERROR, Unit>.asPossibleError() = PossibleError.of(this)
78+
79+
@JvmName("eitherRightErrorAsPossibleError")
80+
fun <ERROR : Any> Either<Unit, ERROR>.asPossibleError() = PossibleError.of(this)
81+
82+
fun <ERROR : Any> Maybe<ERROR>.asPossibleError() = PossibleError.of(this)
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
package com.benromdhane.omar.offroadsoft.monad.error
2+
3+
import com.benromdhane.omar.offroadsoft.monad.Either
4+
import com.benromdhane.omar.offroadsoft.monad.Maybe
5+
import io.kotest.assertions.assertSoftly
6+
import kotlin.test.Test
7+
import kotlin.test.assertEquals
8+
import kotlin.test.assertFalse
9+
import kotlin.test.assertTrue
10+
import kotlin.uuid.ExperimentalUuidApi
11+
import kotlin.uuid.Uuid
12+
13+
@OptIn(ExperimentalUuidApi::class)
14+
class PossibleErrorTest {
15+
16+
@Test
17+
fun `error must return false if possible error is created by try that was initiated as success`() {
18+
val result =
19+
PossibleError.of(Try.seed(Unit))
20+
.error()
21+
22+
assertFalse { result }
23+
}
24+
25+
@Test
26+
fun `error must return true if possible error is created by try that was initiated as failure`() {
27+
val initialError = Exception(Uuid.random().toString())
28+
val result =
29+
PossibleError.of(Try.seed(initialError))
30+
.error()
31+
32+
assertTrue { result }
33+
}
34+
35+
@Test
36+
fun `try as possible error must return success if try was initiated as success`() {
37+
val result =
38+
Try.trying { }
39+
.asPossibleError()
40+
.error()
41+
42+
assertFalse { result }
43+
}
44+
45+
@Test
46+
fun `try as possible error must return error if try was initiated as failure`() {
47+
val initialError = Exception(Uuid.random().toString())
48+
val result =
49+
Try.seed<Unit>(initialError)
50+
.asPossibleError()
51+
.error()
52+
53+
assertTrue { result }
54+
}
55+
56+
@Test
57+
fun `error must return false if possible error is created by either error on left or unit on right that was initiated as right`() {
58+
val result =
59+
PossibleError.of(Either.Right.of<Throwable, _>(Unit))
60+
.error()
61+
62+
assertFalse { result }
63+
}
64+
65+
@Test
66+
fun `error must return true if possible error is created by either error on left or unit on right that was initiated as left`() {
67+
val initialError = Exception(Uuid.random().toString())
68+
val result =
69+
PossibleError.of(Either.Left.of(initialError))
70+
.error()
71+
72+
assertTrue { result }
73+
}
74+
75+
@Test
76+
fun `error must return false if possible error is created by either error on right or unit on left that was initiated as left`() {
77+
val result =
78+
PossibleError.of(Either.Left.of<_, Throwable>(Unit))
79+
.error()
80+
81+
assertFalse { result }
82+
}
83+
84+
@Test
85+
fun `error must return true if possible error is created by either error on right or unit on left that was initiated as right`() {
86+
val initialError = Exception(Uuid.random().toString())
87+
val result =
88+
PossibleError.of(Either.Right.of(initialError))
89+
.error()
90+
91+
assertTrue { result }
92+
}
93+
94+
@Test
95+
fun `either as possible error must return success if try was initiated as right either error on left or unit on right`() {
96+
val result =
97+
Either.Right.of<Throwable, _>(Unit)
98+
.asPossibleError()
99+
.error()
100+
101+
assertFalse { result }
102+
}
103+
104+
@Test
105+
fun `either as possible error must return error if try was initiated as left either error on left or unit on right`() {
106+
val initialError = Exception(Uuid.random().toString())
107+
val result =
108+
Either.Left.of<_, Unit>(initialError)
109+
.asPossibleError()
110+
.error()
111+
112+
assertTrue { result }
113+
}
114+
115+
@Test
116+
fun `either as possible error must return success if try was initiated as left either error on right or unit on left`() {
117+
val result =
118+
Either.Left.of<_, Throwable>(Unit)
119+
.asPossibleError()
120+
.error()
121+
122+
assertFalse { result }
123+
}
124+
125+
@Test
126+
fun `either as possible error must return error if try was initiated as right either error on right or unit on left`() {
127+
val initialError = Exception(Uuid.random().toString())
128+
val result =
129+
Either.Right.of<Unit, _>(initialError)
130+
.asPossibleError()
131+
.error()
132+
133+
assertTrue { result }
134+
}
135+
136+
@Test
137+
fun `error must return false if possible error is created by maybe that was initiated as empty`() {
138+
val result =
139+
PossibleError.of(Maybe.Empty.of<Throwable>())
140+
.error()
141+
142+
assertFalse { result }
143+
}
144+
145+
@Test
146+
fun `error must return true if possible error is created by maybe that was initiated as non empty`() {
147+
val initialError = Exception(Uuid.random().toString())
148+
val result =
149+
PossibleError.of(Maybe.NotEmpty.of(initialError))
150+
.error()
151+
152+
assertTrue { result }
153+
}
154+
155+
@Test
156+
fun `maybe as possible error must return success if try was initiated as empty`() {
157+
val result =
158+
Maybe.Empty.of<Throwable>()
159+
.asPossibleError()
160+
.error()
161+
162+
assertFalse { result }
163+
}
164+
165+
@Test
166+
fun `maybe as possible error must return error if try was initiated as non empty`() {
167+
val initialError = Exception(Uuid.random().toString())
168+
val result =
169+
Maybe.NotEmpty.of(initialError)
170+
.asPossibleError()
171+
.error()
172+
173+
assertTrue { result }
174+
}
175+
176+
@Test
177+
fun `to maybe error must return empty maybe if possible error is created as success`() {
178+
val result =
179+
PossibleError.of(Try.seed(Unit))
180+
.toMaybeError()
181+
.empty()
182+
183+
assertTrue { result }
184+
}
185+
186+
@Test
187+
fun `to maybe error must return not empty maybe with initial value if possible error is created as failure`() {
188+
val initialError = Exception(Uuid.random().toString())
189+
val result =
190+
PossibleError.of(Try.seed(initialError))
191+
.toMaybeError()
192+
.orNull()!!
193+
194+
assertEquals(initialError, result)
195+
}
196+
197+
@Test
198+
fun `map must be ignored if possible error is created as success`() {
199+
var evaluated = false
200+
val mapper: (Throwable) -> Throwable = {
201+
evaluated = true
202+
Exception(Uuid.random().toString())
203+
}
204+
val result =
205+
PossibleError.of(Try.seed(Unit))
206+
.map(mapper)
207+
.error()
208+
209+
assertSoftly {
210+
assertFalse { evaluated }
211+
assertFalse { result }
212+
}
213+
}
214+
215+
@Test
216+
fun `map must transform initial error if possible error is created as error`() {
217+
val initialError = Exception(Uuid.random().toString())
218+
var evaluated = false
219+
val mappedError = Exception(Uuid.random().toString())
220+
val mapper: (Throwable) -> Throwable = {
221+
evaluated = true
222+
mappedError
223+
}
224+
val result =
225+
PossibleError.of(Try.seed(initialError))
226+
.map(mapper)
227+
.toMaybeError()
228+
.orNull()!!
229+
230+
assertSoftly {
231+
assertTrue { evaluated }
232+
assertEquals(mappedError, result)
233+
}
234+
}
235+
236+
@Test
237+
fun `to string must return success if possible error is created as success`() {
238+
val result =
239+
PossibleError.of(Try.seed(Unit))
240+
.toString()
241+
242+
assertEquals("Success", result)
243+
}
244+
245+
@Test
246+
fun `equals must return true if two possible errors are success`() {
247+
val result =
248+
PossibleError.of(Try.seed(Unit))
249+
.equals(PossibleError.of(Try.seed(Unit)))
250+
251+
assertTrue { result }
252+
}
253+
254+
@Test
255+
fun `equals must return true if possible errors compared to itself`() {
256+
val possibleError = PossibleError.of(Try.seed(Unit))
257+
val result =
258+
possibleError
259+
.equals(possibleError)
260+
261+
assertTrue { result }
262+
}
263+
264+
@Test
265+
fun `equals must return false if two possible errors are not both success`() {
266+
val result =
267+
PossibleError.of(Try.seed(Unit))
268+
.equals(
269+
PossibleError.of(
270+
Try.seed(
271+
Exception(Uuid.random().toString())
272+
)
273+
)
274+
)
275+
276+
assertFalse { result }
277+
}
278+
279+
@Test
280+
fun `equals must return false if possible errors is compared to another object`() {
281+
val result =
282+
PossibleError.of(Try.seed(Unit))
283+
.equals(Uuid.random().toString())
284+
285+
assertFalse { result }
286+
}
287+
288+
@Test
289+
fun `equals must return false if possible errors is compared to null`() {
290+
val result =
291+
PossibleError.of(Try.seed(Unit))
292+
.equals(null)
293+
294+
assertFalse { result }
295+
}
296+
}

0 commit comments

Comments
 (0)