Skip to content

Commit a634ae8

Browse files
committed
Use builders instead of data classes
Some of public api data classes are stable (it is not problem) but some of them are extended frequantly. Extended data classes are problem for binary compatibility. Every change is a breaking change. So this commit changes extendable public data classes to builders.
1 parent 1122324 commit a634ae8

File tree

24 files changed

+282
-193
lines changed

24 files changed

+282
-193
lines changed

kstatemachine-coroutines/src/blockingMain/kotlin/ru/nsk/kstatemachine/statemachine/CoroutinesStateMachineBlocking.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ fun createStateMachineBlocking(
2020
name: String? = null,
2121
childMode: ChildMode = ChildMode.EXCLUSIVE,
2222
start: Boolean = true,
23-
creationArguments: StateMachine.CreationArguments = StateMachine.CreationArguments(),
23+
creationArguments: CreationArguments = buildCreationArguments {},
2424
init: suspend BuildingStateMachine.() -> Unit
2525
) = with(CoroutinesLibCoroutineAbstraction(scope)) {
2626
runBlocking {

kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/CoroutinesStateMachine.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ suspend fun createStateMachine(
3232
name: String? = null,
3333
childMode: ChildMode = ChildMode.EXCLUSIVE,
3434
start: Boolean = true,
35-
creationArguments: StateMachine.CreationArguments = StateMachine.CreationArguments(),
35+
creationArguments: CreationArguments = buildCreationArguments {},
3636
init: suspend BuildingStateMachine.() -> Unit
3737
) = CoroutinesLibCoroutineAbstraction(scope)
3838
.createStateMachine(name, childMode, start, creationArguments, init)

kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/LibraryUtils.kt

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import ru.nsk.kstatemachine.state.IState
1111
import ru.nsk.kstatemachine.state.InternalState
1212
import ru.nsk.kstatemachine.statemachine.StateMachine
1313

14+
annotation class VisibleForTesting
15+
1416
/**
1517
* [forEach] analog which ignores internal [StateMachine]s
1618
*/

kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/coroutines/CoroutineAbstraction.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
package ru.nsk.kstatemachine.coroutines
99

1010
import ru.nsk.kstatemachine.state.ChildMode
11-
import ru.nsk.kstatemachine.statemachine.BuildingStateMachine
12-
import ru.nsk.kstatemachine.statemachine.StateMachine
11+
import ru.nsk.kstatemachine.statemachine.*
1312
import ru.nsk.kstatemachine.statemachine.StateMachineImpl
1413
import kotlin.coroutines.Continuation
1514
import kotlin.coroutines.EmptyCoroutineContext
@@ -57,7 +56,7 @@ suspend fun CoroutineAbstraction.createStateMachine(
5756
name: String?,
5857
childMode: ChildMode,
5958
start: Boolean,
60-
creationArguments: StateMachine.CreationArguments = StateMachine.CreationArguments(),
59+
creationArguments: CreationArguments = buildCreationArguments {},
6160
init: suspend BuildingStateMachine.() -> Unit
6261
): StateMachine = StateMachineImpl(
6362
name,

kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/metainfo/MetaInfo.kt

+22-8
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,45 @@ interface MetaInfo
2020
/**
2121
* Standard [MetaInfo], to control export PlantUML and Mermaid feature visualization.
2222
*/
23-
interface IUmlMetaInfo : MetaInfo {
23+
interface UmlMetaInfo : MetaInfo {
2424
/**
2525
* Will be mapped to "long name" for [IState], and a "label" for [Transition]
26+
* Default: null
2627
*/
2728
val umlLabel: String?
29+
2830
/**
2931
* Add description lines for [IState]
3032
* Does not have effect for [Transition]
33+
* Default: emptyList()
3134
*/
3235
val umlStateDescriptions: List<String>
3336

3437
/**
3538
* For [IState] translated to "note right of".
3639
* For [Transition] translated to "note on link" (supports only one note).
3740
* Mermaid does not support this, so it will not take any effect.
41+
* Default: emptyList()
3842
*/
3943
val umlNotes: List<String>
4044
}
4145

4246
/**
43-
* [IUmlMetaInfo] Implementation is separated from its interface as a user may combine multiple [MetaInfo]
44-
* interfaces into one object.
47+
* [UmlMetaInfo] Implementation is separated from its interface as a user may combine multiple [MetaInfo]
48+
* interfaces into one object. Data class should not be exposed to public APIs due to binary compatibility, users should
49+
* use [buildUmlMetaInfo] instead.
4550
*/
46-
data class UmlMetaInfo(
47-
override val umlLabel: String? = null,
48-
override val umlStateDescriptions: List<String> = emptyList(),
49-
override val umlNotes: List<String> = emptyList(),
50-
): IUmlMetaInfo
51+
interface UmlMetaInfoBuilder : UmlMetaInfo {
52+
override var umlLabel: String?
53+
override var umlStateDescriptions: List<String>
54+
override var umlNotes: List<String>
55+
}
56+
57+
private data class UmlMetaInfoBuilderImpl(
58+
override var umlLabel: String? = null,
59+
override var umlStateDescriptions: List<String> = emptyList(),
60+
override var umlNotes: List<String> = emptyList(),
61+
) : UmlMetaInfoBuilder
62+
63+
fun buildUmlMetaInfo(builder: UmlMetaInfoBuilder.() -> Unit): UmlMetaInfo =
64+
UmlMetaInfoBuilderImpl().apply(builder).copy()

kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/persistence/EventRecorder.kt

+43-6
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77

88
package ru.nsk.kstatemachine.persistence
99

10+
import ru.nsk.kstatemachine.VisibleForTesting
1011
import ru.nsk.kstatemachine.event.DestroyEvent
1112
import ru.nsk.kstatemachine.event.StopEvent
1213
import ru.nsk.kstatemachine.statemachine.*
13-
import ru.nsk.kstatemachine.statemachine.StateMachine.EventRecordingArguments
1414
import ru.nsk.kstatemachine.transition.EventAndArgument
1515
import ru.nsk.kstatemachine.visitors.structureHashCode
1616

@@ -23,17 +23,54 @@ interface EventRecorder {
2323
}
2424

2525
/**
26-
* This object is intended to be serialized by client code
26+
* This class is intended to be serialized by client code
2727
*/
28-
data class RecordedEvents(
28+
class RecordedEvents @VisibleForTesting constructor(
2929
val structureHashCode: Int,
3030
val records: List<Record>,
31-
)
31+
) {
3232

33-
data class Record(
33+
override fun equals(other: Any?): Boolean {
34+
if (this === other) return true
35+
if (other == null || this::class != other::class) return false
36+
37+
other as RecordedEvents
38+
39+
if (structureHashCode != other.structureHashCode) return false
40+
if (records != other.records) return false
41+
42+
return true
43+
}
44+
45+
override fun hashCode(): Int {
46+
var result = structureHashCode
47+
result = 31 * result + records.hashCode()
48+
return result
49+
}
50+
}
51+
52+
class Record @VisibleForTesting constructor(
3453
val eventAndArgument: EventAndArgument<*>,
3554
val processingResult: ProcessingResult,
36-
)
55+
) {
56+
override fun equals(other: Any?): Boolean {
57+
if (this === other) return true
58+
if (other == null || this::class != other::class) return false
59+
60+
other as Record
61+
62+
if (eventAndArgument != other.eventAndArgument) return false
63+
if (processingResult != other.processingResult) return false
64+
65+
return true
66+
}
67+
68+
override fun hashCode(): Int {
69+
var result = eventAndArgument.hashCode()
70+
result = 31 * result + processingResult.hashCode()
71+
return result
72+
}
73+
}
3774

3875
internal class EventRecorderImpl(
3976
private val machine: StateMachine,

kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/persistence/RestoreByRecordedEvents.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ import ru.nsk.kstatemachine.event.StartEvent
1111
import ru.nsk.kstatemachine.statemachine.*
1212
import ru.nsk.kstatemachine.visitors.structureHashCode
1313

14-
data class RestorationResult(
14+
class RestorationResult internal constructor(
1515
val results: List<RestoredEventResult>,
1616
val warnings: List<RestorationWarningException>,
1717
)
1818

19-
data class RestoredEventResult(
19+
class RestoredEventResult internal constructor(
2020
val record: Record,
2121
val processingResult: Result<ProcessingResult>,
2222
val warnings: List<RestorationWarningException>,
@@ -27,7 +27,7 @@ enum class WarningType {
2727
RecordedAndProcessedEventCountNotMatch,
2828
}
2929

30-
class RestorationWarningException(
30+
class RestorationWarningException internal constructor(
3131
val warningType: WarningType,
3232
message: String,
3333
cause: Throwable? = null,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Author: Mikhail Fedotov
3+
* Github: https://github.com/KStateMachine
4+
* Copyright (c) 2024.
5+
* All rights reserved.
6+
*/
7+
8+
package ru.nsk.kstatemachine.statemachine
9+
10+
interface CreationArguments {
11+
/**
12+
* Allows the library to automatically call destroy() on current state owning machine instance if user tries
13+
* to reuse its states in another machine. Usually this is a result of using object states in sequentially created
14+
* similar machines. destroy() will be called on the previous machine instance.
15+
* If set to false an exception will be thrown on state reuse attempt.
16+
* Default: true
17+
*/
18+
val autoDestroyOnStatesReuse: Boolean
19+
20+
/**
21+
* Enables Undo transition.
22+
* Default: false
23+
*/
24+
val isUndoEnabled: Boolean
25+
26+
/**
27+
* If set to true, when multiple transitions match event the first matching transition is selected.
28+
* if set to false, when multiple transitions match event exception is thrown.
29+
* Default: false
30+
*/
31+
val doNotThrowOnMultipleTransitionsMatch: Boolean
32+
33+
/**
34+
* If enabled, throws exception on the machine start,
35+
* if it contains states or transitions with null or blank names
36+
* Default: false
37+
*/
38+
val requireNonBlankNames: Boolean
39+
40+
/**
41+
* If set, enables incoming events recording in order to restore [StateMachine] later.
42+
* By default, event recording is disabled.
43+
* Use [StateMachine.eventRecorder] to access the recording result.
44+
* Default: null
45+
*/
46+
val eventRecordingArguments: EventRecordingArguments?
47+
}
48+
49+
interface CreationArgumentsBuilder : CreationArguments {
50+
override var autoDestroyOnStatesReuse: Boolean
51+
override var isUndoEnabled: Boolean
52+
override var doNotThrowOnMultipleTransitionsMatch: Boolean
53+
override var requireNonBlankNames: Boolean
54+
override var eventRecordingArguments: EventRecordingArguments?
55+
}
56+
57+
private data class CreationArgumentsBuilderImpl(
58+
override var autoDestroyOnStatesReuse: Boolean = true,
59+
override var isUndoEnabled: Boolean = false,
60+
override var doNotThrowOnMultipleTransitionsMatch: Boolean = false,
61+
override var requireNonBlankNames: Boolean = false,
62+
override var eventRecordingArguments: EventRecordingArguments? = null
63+
) : CreationArgumentsBuilder
64+
65+
fun buildCreationArguments(builder: CreationArgumentsBuilder.() -> Unit): CreationArguments =
66+
CreationArgumentsBuilderImpl().apply(builder).copy()
67+
68+
interface EventRecordingArguments {
69+
/**
70+
* If enabled removes all recorded events when detects that the machine was stopped and started again.
71+
* Default: true
72+
*/
73+
val clearRecordsOnMachineRestart: Boolean
74+
75+
/**
76+
* If enabled skips ignored events, supposing they do not affect restoration of the machine
77+
* Default: true
78+
*/
79+
val skipIgnoredEvents: Boolean
80+
}
81+
82+
interface EventRecordingArgumentsBuilder : EventRecordingArguments {
83+
override var clearRecordsOnMachineRestart: Boolean
84+
override var skipIgnoredEvents: Boolean
85+
}
86+
87+
private data class EventRecordingArgumentsBuilderImpl(
88+
override var clearRecordsOnMachineRestart: Boolean = true,
89+
override var skipIgnoredEvents: Boolean = true,
90+
) : EventRecordingArgumentsBuilder
91+
92+
fun buildEventRecordingArguments(builder: EventRecordingArgumentsBuilder.() -> Unit): EventRecordingArguments =
93+
EventRecordingArgumentsBuilderImpl().apply(builder).copy()

kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/StateMachine.kt

+1-43
Original file line numberDiff line numberDiff line change
@@ -158,48 +158,6 @@ interface StateMachine : State {
158158
*/
159159
suspend fun onException(exception: Exception)
160160
}
161-
162-
data class CreationArguments(
163-
/**
164-
* Allows the library to automatically call destroy() on current state owning machine instance if user tries
165-
* to reuse its states in another machine. Usually this is a result of using object states in sequentially created
166-
* similar machines. destroy() will be called on the previous machine instance.
167-
* If set to false an exception will be thrown on state reuse attempt.
168-
*/
169-
val autoDestroyOnStatesReuse: Boolean = true,
170-
/**
171-
* Enables Undo transition
172-
*/
173-
val isUndoEnabled: Boolean = false,
174-
/**
175-
* If set to true, when multiple transitions match event the first matching transition is selected.
176-
* if set to false, when multiple transitions match event exception is thrown.
177-
* Default if false.
178-
*/
179-
val doNotThrowOnMultipleTransitionsMatch: Boolean = false,
180-
/**
181-
* If enabled, throws exception on the machine start,
182-
* if it contains states or transitions with null or blank names
183-
*/
184-
val requireNonBlankNames: Boolean = false,
185-
/**
186-
* If set, enables incoming events recording in order to restore [StateMachine] later.
187-
* By default, event recording is disabled.
188-
* Use [StateMachine.eventRecorder] to access the recording result.
189-
*/
190-
val eventRecordingArguments: EventRecordingArguments? = null
191-
)
192-
193-
data class EventRecordingArguments(
194-
/**
195-
* If enabled removes all recorded events when detects that the machine was stopped and started again.
196-
*/
197-
val clearRecordsOnMachineRestart: Boolean = true,
198-
/**
199-
* If enabled skips ignored events, supposing they do not affect restoration of the machine
200-
*/
201-
val skipIgnoredEvents: Boolean = true,
202-
)
203161
}
204162

205163
fun StateMachine.startBlocking(argument: Any? = null) = coroutineAbstraction.runBlocking { start(argument) }
@@ -288,7 +246,7 @@ fun createStdLibStateMachine(
288246
name: String? = null,
289247
childMode: ChildMode = ChildMode.EXCLUSIVE,
290248
start: Boolean = true,
291-
creationArguments: StateMachine.CreationArguments = StateMachine.CreationArguments(),
249+
creationArguments: CreationArguments = buildCreationArguments {},
292250
init: suspend BuildingStateMachine.() -> Unit
293251
): StateMachine {
294252
return with(StdLibCoroutineAbstraction()) {

kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineImpl.kt

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import ru.nsk.kstatemachine.persistence.EventRecorderImpl
1515
import ru.nsk.kstatemachine.state.*
1616
import ru.nsk.kstatemachine.state.pseudo.UndoState
1717
import ru.nsk.kstatemachine.statemachine.ProcessingResult.*
18-
import ru.nsk.kstatemachine.statemachine.StateMachine.CreationArguments
1918
import ru.nsk.kstatemachine.transition.*
2019
import ru.nsk.kstatemachine.transition.TransitionDirectionProducerPolicy.DefaultPolicy
2120
import ru.nsk.kstatemachine.visitors.CheckUniqueNamesVisitor

kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/transition/TransitionParams.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import ru.nsk.kstatemachine.event.WrappedEvent
1212
import ru.nsk.kstatemachine.statemachine.StateMachineDslMarker
1313

1414
@StateMachineDslMarker
15-
data class TransitionParams<E : Event>(
15+
data class TransitionParams<E : Event> internal constructor(
1616
val transition: Transition<E>,
1717
val direction: TransitionDirection,
1818
val event: E,

0 commit comments

Comments
 (0)