diff --git a/CHANGELOG.md b/CHANGELOG.md index 57a254ea..46664df7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## 0.0.16 +* `Versions`: + * `MicroUtils`: `0.17.5` * `NavigationChain` got its own optional id * `NavigationChain` and `NavigationNode` got `findChain` extension * `NavigationChain` and `NavigationNode` got `findNode` extension diff --git a/core/src/commonMain/kotlin/extensions/Chain.kt b/core/src/commonMain/kotlin/extensions/Chain.kt index 11798fc8..53129564 100644 --- a/core/src/commonMain/kotlin/extensions/Chain.kt +++ b/core/src/commonMain/kotlin/extensions/Chain.kt @@ -40,27 +40,45 @@ fun NavigationChain.findChain(id: NavigationChainId): NavigationCha // Drop/replace/push by node id +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[dropInSubTree] + */ fun NavigationChain.dropInSubTree( id: NavigationNodeId ): Boolean = chainOrNodeEither().dropInSubTree(id) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[dropNodeInSubTree] + */ fun NavigationChain.dropNodeInSubTree(id: String) = chainOrNodeEither().dropNodeInSubTree(id) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[replaceInSubTree] + */ fun NavigationChain.replaceInSubTree( id: NavigationNodeId, config: Base, ): Boolean = chainOrNodeEither().replaceInSubTree(id, config) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[replaceInSubTree] + */ fun NavigationChain.replaceInSubTree( id: String, config: Base ) = chainOrNodeEither().replaceInSubTree(id, config) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[pushInSubTree] + */ fun NavigationChain.pushInSubTree( inChainWith: NavigationNodeId, config: Base ): Boolean = chainOrNodeEither().pushInSubTree(inChainWith, config) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[pushInSubTreeByNodeId] + */ fun NavigationChain.pushInSubTreeByNodeId( inChainWithNodeId: String, config: Base @@ -68,19 +86,31 @@ fun NavigationChain.pushInSubTreeByNodeId( // Drop/push by chain id +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[dropInSubTree] + */ fun NavigationChain.dropInSubTree( id: NavigationChainId ) = chainOrNodeEither().dropInSubTree(id) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[dropChainInSubTree] + */ fun NavigationChain.dropChainInSubTree( id: String ) = chainOrNodeEither().dropChainInSubTree(id) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[pushInSubTree] + */ fun NavigationChain.pushInSubTree( inChainWithNodeId: NavigationChainId, config: Base ) = chainOrNodeEither().pushInSubTree(inChainWithNodeId, config) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[pushInSubTreeByChainId] + */ fun NavigationChain.pushInSubTreeByChainId( inChainWithNodeId: String, config: Base diff --git a/core/src/commonMain/kotlin/extensions/EitherChainOrNode.kt b/core/src/commonMain/kotlin/extensions/EitherChainOrNode.kt index ec025547..e6963fa3 100644 --- a/core/src/commonMain/kotlin/extensions/EitherChainOrNode.kt +++ b/core/src/commonMain/kotlin/extensions/EitherChainOrNode.kt @@ -3,13 +3,17 @@ package dev.inmo.navigation.core.extensions import dev.inmo.navigation.core.ChainOrNodeEither import dev.inmo.navigation.core.NavigationChainId import dev.inmo.navigation.core.NavigationNodeId -import dev.inmo.navigation.core.visiter.walk import dev.inmo.navigation.core.visiter.walkOnChains import dev.inmo.navigation.core.visiter.walkOnNodes // Drop/replace/push by chain id +/** + * Will drop all nodes in tree with [dev.inmo.navigation.core.NavigationNode.id] == [id] + * + * **This method will start its work with [this] as a root** + */ fun ChainOrNodeEither.dropInSubTree( id: NavigationNodeId ): Boolean { @@ -22,8 +26,17 @@ fun ChainOrNodeEither.dropInSubTree( return deleted } +/** + * Shortcut for [dropInSubTree] + */ fun ChainOrNodeEither.dropNodeInSubTree(id: String) = dropInSubTree(NavigationNodeId(id)) +/** + * Will [dev.inmo.navigation.core.NavigationChain.replace] all nodes in tree with + * [dev.inmo.navigation.core.NavigationNode.id] == [id] by a new one with [config] + * + * **This method will start its work with [this] as a root** + */ fun ChainOrNodeEither.replaceInSubTree( id: NavigationNodeId, config: Base, @@ -35,24 +48,36 @@ fun ChainOrNodeEither.replaceInSubTree( return replaced } +/** + * Shortcut for method [replaceInSubTree] + */ fun ChainOrNodeEither.replaceInSubTree( id: String, config: Base ) = replaceInSubTree(NavigationNodeId(id), config) +/** + * Will push on top in all chains with any [dev.inmo.navigation.core.NavigationNode] in + * [dev.inmo.navigation.core.NavigationChain.stack] with [dev.inmo.navigation.core.NavigationNode.id] == [id] + * + * **This method will start its work with [this] as a root** + */ fun ChainOrNodeEither.pushInSubTree( - inChainWith: NavigationNodeId, + id: NavigationNodeId, config: Base ): Boolean { var pushed = false walkOnNodes { - if (it.id == inChainWith) { + if (it.id == id) { pushed = it.chain.push(config) != null || pushed } } return pushed } +/** + * Shortcut for method [pushInSubTree] + */ fun ChainOrNodeEither.pushInSubTreeByNodeId( inChainWithNodeId: String, config: Base @@ -60,6 +85,11 @@ fun ChainOrNodeEither.pushInSubTreeByNodeId( // Drop/push by chain id +/** + * Will drop all chains in tree with [dev.inmo.navigation.core.NavigationChain.id] == [id] + * + * **This method will start its work with [this] as a root** + */ fun ChainOrNodeEither.dropInSubTree( id: NavigationChainId ): Boolean { @@ -73,21 +103,32 @@ fun ChainOrNodeEither.dropInSubTree( return deleted } +/** + * Shortcut for method [dropInSubTree] + */ fun ChainOrNodeEither.dropChainInSubTree(id: String) = dropInSubTree(NavigationChainId(id)) +/** + * Will push on top in all chains with [dev.inmo.navigation.core.NavigationChain.id] == [id] + * + * **This method will start its work with [this] as a root** + */ fun ChainOrNodeEither.pushInSubTree( - inChainWith: NavigationChainId, + id: NavigationChainId, config: Base ): Boolean { var pushed = false walkOnChains { - if (it.id == inChainWith) { + if (it.id == id) { pushed = it.push(config) != null || pushed } } return pushed } +/** + * Shortcut for method [pushInSubTree] + */ fun ChainOrNodeEither.pushInSubTreeByChainId( inChainWithNodeId: String, config: Base diff --git a/core/src/commonMain/kotlin/extensions/Node.kt b/core/src/commonMain/kotlin/extensions/Node.kt index 9674b8e5..0a8ee226 100644 --- a/core/src/commonMain/kotlin/extensions/Node.kt +++ b/core/src/commonMain/kotlin/extensions/Node.kt @@ -40,27 +40,45 @@ fun NavigationNode<*, Base>.findChain(id: NavigationChainId): NavigationC // Drop/replace/push by node id +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[dropInSubTree] + */ fun NavigationNode<*, Base>.dropInSubTree( id: NavigationNodeId ): Boolean = chainOrNodeEither().dropInSubTree(id) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[dropNodeInSubTree] + */ fun NavigationNode<*, Base>.dropNodeInSubTree(id: String) = chainOrNodeEither().dropNodeInSubTree(id) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[replaceInSubTree] + */ fun NavigationNode<*, Base>.replaceInSubTree( id: NavigationNodeId, config: Base, ): Boolean = chainOrNodeEither().replaceInSubTree(id, config) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[replaceInSubTree] + */ fun NavigationNode<*, Base>.replaceInSubTree( id: String, config: Base ) = chainOrNodeEither().replaceInSubTree(id, config) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[pushInSubTree] + */ fun NavigationNode<*, Base>.pushInSubTree( inChainWith: NavigationNodeId, config: Base ): Boolean = chainOrNodeEither().pushInSubTree(inChainWith, config) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[pushInSubTreeByNodeId] + */ fun NavigationNode<*, Base>.pushInSubTreeByNodeId( inChainWithNodeId: String, config: Base @@ -68,19 +86,31 @@ fun NavigationNode<*, Base>.pushInSubTreeByNodeId( // Drop/push by chain id +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[dropInSubTree] + */ fun NavigationNode<*, Base>.dropInSubTree( id: NavigationChainId ) = chainOrNodeEither().dropInSubTree(id) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[dropChainInSubTree] + */ fun NavigationNode<*, Base>.dropChainInSubTree( id: String ) = chainOrNodeEither().dropChainInSubTree(id) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[pushInSubTree] + */ fun NavigationNode<*, Base>.pushInSubTree( inChainWithNodeId: NavigationChainId, config: Base ) = chainOrNodeEither().pushInSubTree(inChainWithNodeId, config) +/** + * Shortcut for method [dev.inmo.navigation.core.ChainOrNodeEither].[pushInSubTreeByChainId] + */ fun NavigationNode<*, Base>.pushInSubTreeByChainId( inChainWithNodeId: String, config: Base diff --git a/core/src/commonMain/kotlin/visiter/NavigationTreeWalker.kt b/core/src/commonMain/kotlin/visiter/NavigationTreeWalker.kt deleted file mode 100644 index ce53d681..00000000 --- a/core/src/commonMain/kotlin/visiter/NavigationTreeWalker.kt +++ /dev/null @@ -1,147 +0,0 @@ -package dev.inmo.navigation.core.visiter - -import dev.inmo.micro_utils.common.Either -import dev.inmo.micro_utils.common.either -import dev.inmo.micro_utils.common.onFirst -import dev.inmo.micro_utils.common.onSecond -import dev.inmo.navigation.core.ChainOrNodeEither -import dev.inmo.navigation.core.NavigationChain -import dev.inmo.navigation.core.NavigationNode -import dev.inmo.navigation.core.chainOrNodeEither -import dev.inmo.navigation.core.onChain -import dev.inmo.navigation.core.onNode -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow - -/** - * Will walk on whole tree of navigation. The order of walking next: - * - * * First faced [NavigationNode] will be passed to the [onNode] first - * * First faced [NavigationChain] will be passed to the [onChain] first - * - * @receiver [Either] node or chain which assumed as a root for walking and will be used as first parameter for - * calling of [onChain] or [onNode] - * @param onNode Will be passed in call [NavigationNode.walk] - * @param onChain Will be called for [this] [NavigationChain] and passed in [NavigationNode.walk] - */ -inline fun ChainOrNodeEither.walk( - onNodeOrChain: NavigationNodeOrChainVisitingCallback -) { - val visitingQueue = ArrayDeque>() - visitingQueue.add(this) - - while (visitingQueue.isNotEmpty()) { - val firstOne = visitingQueue.removeFirst() - onNodeOrChain(firstOne) - - firstOne.onFirst { - visitingQueue.addAll(0, it.stackFlow.value.map { it.chainOrNodeEither() }) - }.onSecond { - visitingQueue.addAll(0, it.subchainsFlow.value.map { it.chainOrNodeEither() }) - } - } -} - -/** - * Will walk on whole tree of navigation. **This function will start since [this] [NavigationChain]** - * - * @param onNode Will be passed in call [NavigationNode.walk] - * @param onChain Will be called for [this] [NavigationChain] and passed in [NavigationNode.walk] - */ -inline fun NavigationChain.walk( - onNodeOrChain: NavigationNodeOrChainVisitingCallback -) = chainOrNodeEither().walk(onNodeOrChain) - -/** - * Will walk on whole tree of navigation. **This function will start since [this] [NavigationChain]** - * - * @param onNode Will be passed in call [NavigationNode.walk] - * @param onChain Will be called for [this] [NavigationChain] and passed in [NavigationNode.walk] - */ -inline fun NavigationNode.walk( - onNodeOrChain: NavigationNodeOrChainVisitingCallback -) = chainOrNodeEither().walk(onNodeOrChain) - -/** - * Will walk on whole tree of navigation. The order of walking next: - * - * * First faced [NavigationNode] will be passed to the [onNode] first - * * First faced [NavigationChain] will be passed to the [onChain] first - * - * @receiver [Either] node or chain which assumed as a root for walking and will be used as first parameter for - * calling of [onChain] or [onNode] - * @param onNode Will be passed in call [NavigationNode.walk] - * @param onChain Will be called for [this] [NavigationChain] and passed in [NavigationNode.walk] - */ -inline fun ChainOrNodeEither.walkOnChains( - onChain: NavigationChainVisitingCallback -) = walk { it.onChain(onChain) } - -/** - * Will walk on whole tree of navigation. **This function will start since [this] [NavigationChain]** - * - * @param onNode Will be passed in call [NavigationNode.walk] - * @param onChain Will be called for [this] [NavigationChain] and passed in [NavigationNode.walk] - */ -inline fun NavigationChain.walkOnChains( - onChain: NavigationChainVisitingCallback -) = chainOrNodeEither().walkOnChains(onChain) - -/** - * Will walk on whole tree of navigation. **This function will start since [this] [NavigationChain]** - * - * @param onNode Will be passed in call [NavigationNode.walk] - * @param onChain Will be called for [this] [NavigationChain] and passed in [NavigationNode.walk] - */ -inline fun NavigationNode.walkOnChains( - onChain: NavigationChainVisitingCallback -) = chainOrNodeEither().walkOnChains(onChain) - -/** - * Will walk on whole tree of navigation. The order of walking next: - * - * * First faced [NavigationNode] will be passed to the [onNode] first - * * First faced [NavigationChain] will be passed to the [onChain] first - * - * @receiver [Either] node or chain which assumed as a root for walking and will be used as first parameter for - * calling of [onChain] or [onNode] - * @param onNode Will be passed in call [NavigationNode.walk] - * @param onChain Will be called for [this] [NavigationChain] and passed in [NavigationNode.walk] - */ -inline fun ChainOrNodeEither.walkOnNodes( - onNode: NavigationNodeVisitingCallback -) = walk { it.onNode(onNode) } - -/** - * Will walk on whole tree of navigation. **This function will start since [this] [NavigationChain]** - * - * @param onNode Will be passed in call [NavigationNode.walk] - * @param onChain Will be called for [this] [NavigationChain] and passed in [NavigationNode.walk] - */ -inline fun NavigationChain.walkOnNodes( - onNode: NavigationNodeVisitingCallback -) = chainOrNodeEither().walkOnNodes(onNode) - -/** - * Will walk on whole tree of navigation. **This function will start since [this] [NavigationChain]** - * - * @param onNode Will be passed in call [NavigationNode.walk] - * @param onChain Will be called for [this] [NavigationChain] and passed in [NavigationNode.walk] - */ -inline fun NavigationNode.walkOnNodes( - onNode: NavigationNodeVisitingCallback -) = chainOrNodeEither().walkOnNodes(onNode) - -fun ChainOrNodeEither.walkFlow(): Flow> { - return flow { - walk { emit(it) } - } -} - -fun NavigationChain.walkFlow(): Flow> { - return chainOrNodeEither().walkFlow() -} - -fun NavigationNode.walkFlow(): Flow> { - return chainOrNodeEither().walkFlow() -} diff --git a/core/src/commonMain/kotlin/visiter/Walk.kt b/core/src/commonMain/kotlin/visiter/Walk.kt new file mode 100644 index 00000000..b70a863c --- /dev/null +++ b/core/src/commonMain/kotlin/visiter/Walk.kt @@ -0,0 +1,57 @@ +package dev.inmo.navigation.core.visiter + +import dev.inmo.micro_utils.common.onFirst +import dev.inmo.micro_utils.common.onSecond +import dev.inmo.navigation.core.ChainOrNodeEither +import dev.inmo.navigation.core.NavigationChain +import dev.inmo.navigation.core.NavigationNode +import dev.inmo.navigation.core.chainOrNodeEither + + +/** + * Root fun for walking across the navigation tree starting with [this] as a root. + * + * **This function is not recursive** + * + * * Each met [NavigationNode] will be passed to [onNodeOrChain] and its [NavigationNode.subchains] will be added + * __in the beginning__ of visiting queue + * * Each met [NavigationChain] will be passed to [onNodeOrChain] and its [NavigationChain.stack] will be added + * __in the beginning__ of visiting queue + * + * None happen of the [onNodeOrChain] exceptions will be stopped. + * + * @see walkFlow + * @see walkOnChains + * @see walkOnNodes + */ +inline fun ChainOrNodeEither.walk( + onNodeOrChain: NavigationNodeOrChainVisitingCallback +) { + val visitingQueue = ArrayDeque>() + visitingQueue.add(this) + + while (visitingQueue.isNotEmpty()) { + val firstOne = visitingQueue.removeFirst() + onNodeOrChain(firstOne) + + firstOne.onFirst { + visitingQueue.addAll(0, it.stackFlow.value.map { it.chainOrNodeEither() }) + }.onSecond { + visitingQueue.addAll(0, it.subchainsFlow.value.map { it.chainOrNodeEither() }) + } + } +} + +/** + * Shortcut for main [ChainOrNodeEither].[walk] + */ +inline fun NavigationChain.walk( + onNodeOrChain: NavigationNodeOrChainVisitingCallback +) = chainOrNodeEither().walk(onNodeOrChain) + +/** + * Shortcut for main [ChainOrNodeEither].[walk] + */ +inline fun NavigationNode.walk( + onNodeOrChain: NavigationNodeOrChainVisitingCallback +) = chainOrNodeEither().walk(onNodeOrChain) diff --git a/core/src/commonMain/kotlin/visiter/WalkFlow.kt b/core/src/commonMain/kotlin/visiter/WalkFlow.kt new file mode 100644 index 00000000..8e80e675 --- /dev/null +++ b/core/src/commonMain/kotlin/visiter/WalkFlow.kt @@ -0,0 +1,33 @@ +package dev.inmo.navigation.core.visiter + +import dev.inmo.navigation.core.ChainOrNodeEither +import dev.inmo.navigation.core.NavigationChain +import dev.inmo.navigation.core.NavigationNode +import dev.inmo.navigation.core.chainOrNodeEither +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow + + +/** + * Creates flow with visited [NavigationNode]s and [NavigationChain]s with [walk] throw whole tree using [this] + * as root + */ +fun ChainOrNodeEither.walkFlow(): Flow> { + return flow { + walk { emit(it) } + } +} + +/** + * Shortcut for main [ChainOrNodeEither].[walkFlow] + */ +fun NavigationChain.walkFlow(): Flow> { + return chainOrNodeEither().walkFlow() +} + +/** + * Shortcut for main [ChainOrNodeEither].[walkFlow] + */ +fun NavigationNode.walkFlow(): Flow> { + return chainOrNodeEither().walkFlow() +} diff --git a/core/src/commonMain/kotlin/visiter/WalkOnChains.kt b/core/src/commonMain/kotlin/visiter/WalkOnChains.kt new file mode 100644 index 00000000..1fe300c9 --- /dev/null +++ b/core/src/commonMain/kotlin/visiter/WalkOnChains.kt @@ -0,0 +1,29 @@ +package dev.inmo.navigation.core.visiter + +import dev.inmo.navigation.core.ChainOrNodeEither +import dev.inmo.navigation.core.NavigationChain +import dev.inmo.navigation.core.NavigationNode +import dev.inmo.navigation.core.chainOrNodeEither +import dev.inmo.navigation.core.onChain + +/** + * Uses [walk] to visit whole navigation tree with [this] as root, but will pass in [onChain] lambda only visited + * [NavigationChain]s + */ +inline fun ChainOrNodeEither.walkOnChains( + onChain: NavigationChainVisitingCallback +) = walk { it.onChain(onChain) } + +/** + * Shortcut for main [ChainOrNodeEither].[walkOnChains] + */ +inline fun NavigationChain.walkOnChains( + onChain: NavigationChainVisitingCallback +) = chainOrNodeEither().walkOnChains(onChain) + +/** + * Shortcut for main [ChainOrNodeEither].[walkOnChains] + */ +inline fun NavigationNode.walkOnChains( + onChain: NavigationChainVisitingCallback +) = chainOrNodeEither().walkOnChains(onChain) diff --git a/core/src/commonMain/kotlin/visiter/WalkOnNodes.kt b/core/src/commonMain/kotlin/visiter/WalkOnNodes.kt new file mode 100644 index 00000000..92fd6781 --- /dev/null +++ b/core/src/commonMain/kotlin/visiter/WalkOnNodes.kt @@ -0,0 +1,30 @@ +package dev.inmo.navigation.core.visiter + +import dev.inmo.navigation.core.ChainOrNodeEither +import dev.inmo.navigation.core.NavigationChain +import dev.inmo.navigation.core.NavigationNode +import dev.inmo.navigation.core.chainOrNodeEither +import dev.inmo.navigation.core.onNode + + +/** + * Uses [walk] to visit whole navigation tree with [this] as root, but will pass in [onNode] lambda only visited + * [NavigationNode]s + */ +inline fun ChainOrNodeEither.walkOnNodes( + onNode: NavigationNodeVisitingCallback +) = walk { it.onNode(onNode) } + +/** + * Shortcut for main [ChainOrNodeEither].[walkOnNodes] + */ +inline fun NavigationChain.walkOnNodes( + onNode: NavigationNodeVisitingCallback +) = chainOrNodeEither().walkOnNodes(onNode) + +/** + * Shortcut for main [ChainOrNodeEither].[walkOnNodes] + */ +inline fun NavigationNode.walkOnNodes( + onNode: NavigationNodeVisitingCallback +) = chainOrNodeEither().walkOnNodes(onNode) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f582e83d..5953732e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ kotlin-coroutines = "1.6.4" kotlin-serialization = "1.5.0" dokka = "1.8.10" -microutils = "0.17.2" +microutils = "0.17.5" kslog = "1.0.0" uuid = "0.7.0"