diff --git a/src/nativeMain/kotlin/Main.kt b/src/nativeMain/kotlin/Main.kt index dd50804..471101c 100644 --- a/src/nativeMain/kotlin/Main.kt +++ b/src/nativeMain/kotlin/Main.kt @@ -5,13 +5,21 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import logger.Logger import logger.context +import platform.posix.exit import userInput.Arguments import userInput.Keymap import userInput.Keymap.Actions @OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class) fun main(args: Array) { - Arguments.validate(args) + val appModule = AppModule() + + try { + appModule.arguments.validate(args) + } catch (e: Arguments.ValidationException) { + println(e.message) + exit(1) + } val ui = newSingleThreadContext("UI") @@ -24,8 +32,6 @@ fun main(args: Array) { //he key takeaway is that if you call launch on a custom CoroutineScope, any CoroutineExceptionHandler provided // directly to the CoroutineScope constructor or to launch will be executed when an exception is thrown within the launched coroutine. val appJob = CoroutineScope(ui).launch(handler) { - val appModule = AppModule() - with(appModule) { Logger.set(fileLogger) diff --git a/src/nativeMain/kotlin/di/AppModule.kt b/src/nativeMain/kotlin/di/AppModule.kt index a77577b..37d29ed 100644 --- a/src/nativeMain/kotlin/di/AppModule.kt +++ b/src/nativeMain/kotlin/di/AppModule.kt @@ -5,7 +5,7 @@ import BuildConfig import FileLogger import InternalAppState import di.DogcatModule.dogcatModule -import kotlinx.coroutines.CloseableCoroutineDispatcher +import kotlinx.cli.ArgParser import logger.CanLog import logger.NoOpLogger import org.kodein.di.DI @@ -14,6 +14,7 @@ import org.kodein.di.instance import ui.AppPresenter import ui.logLines.LogLinesPresenter import ui.status.StatusPresenter +import userInput.Arguments import userInput.DefaultInput import userInput.Input @@ -27,6 +28,7 @@ class AppModule { NoOpLogger() } } + bindSingleton { Arguments(ArgParser("dogcat")) } bindSingleton { InternalAppState() } bindSingleton { DefaultInput(instance()) } bindSingleton { @@ -35,6 +37,7 @@ class AppModule { instance(), instance(), instance(), + instance(), instance() ) } @@ -47,6 +50,7 @@ class AppModule { } bindSingleton { LogLinesPresenter( + instance(), instance(), instance(), instance() @@ -61,6 +65,8 @@ class AppModule { val fileLogger: CanLog by serviceLocator.instance() + val arguments: Arguments by serviceLocator.instance() + val input: Input by serviceLocator.instance() val appPresenter: AppPresenter by serviceLocator.instance() diff --git a/src/nativeMain/kotlin/ui/AppPresenter.kt b/src/nativeMain/kotlin/ui/AppPresenter.kt index 487fbe9..5c2e418 100644 --- a/src/nativeMain/kotlin/ui/AppPresenter.kt +++ b/src/nativeMain/kotlin/ui/AppPresenter.kt @@ -10,11 +10,13 @@ import dogcat.state.PublicState import dogcat.state.PublicState.Active import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.launch import logger.Logger import logger.context -import ui.Strings.INPUT_FILTER_PREFIX import ui.logLines.LogLinesPresenter import ui.status.StatusPresenter import userInput.Arguments @@ -25,6 +27,7 @@ import kotlin.coroutines.coroutineContext class AppPresenter( private val dogcat: Dogcat, + private val arguments: Arguments, private val appState: AppState, private val input: Input, private val logLinesPresenter: LogLinesPresenter, @@ -35,8 +38,8 @@ class AppPresenter( override suspend fun start() { when { - Arguments.packageName != null -> dogcat(PickAppPackage(Arguments.packageName!!)) - Arguments.current == true -> dogcat(PickForegroundApp) + arguments.packageName != null -> dogcat(PickAppPackage(arguments.packageName!!)) + arguments.current == true -> dogcat(PickForegroundApp) else -> dogcat(PickAllApps) } diff --git a/src/nativeMain/kotlin/ui/logLines/LogLinesPresenter.kt b/src/nativeMain/kotlin/ui/logLines/LogLinesPresenter.kt index 7ad51f5..8891315 100644 --- a/src/nativeMain/kotlin/ui/logLines/LogLinesPresenter.kt +++ b/src/nativeMain/kotlin/ui/logLines/LogLinesPresenter.kt @@ -19,6 +19,7 @@ import kotlin.coroutines.coroutineContext class LogLinesPresenter( private val dogcat: Dogcat, + private val arguments: Arguments, private val appState: AppState, private val input: Input, ) : HasLifecycle { @@ -55,7 +56,7 @@ class LogLinesPresenter( .collect { view.state = view.state.copy( autoscroll = it.autoscroll, - showLineNumbers = Arguments.lineNumbers ?: false, + showLineNumbers = arguments.lineNumbers ?: false, isCursorHeld = it.isCursorHeld, cursorReturnLocation = it.inputFilterLocation ) diff --git a/src/nativeMain/kotlin/userInput/Arguments.kt b/src/nativeMain/kotlin/userInput/Arguments.kt index 7dec4d3..33a4f0a 100644 --- a/src/nativeMain/kotlin/userInput/Arguments.kt +++ b/src/nativeMain/kotlin/userInput/Arguments.kt @@ -4,18 +4,34 @@ import kotlinx.cli.ArgParser import kotlinx.cli.ArgType import kotlinx.cli.optional -object Arguments { +class Arguments(private val parser: ArgParser) { - private val parser = ArgParser("dogcat") + class ValidationException(override val message: String) : RuntimeException(message) - val packageName by parser.argument(ArgType.String, "package name", "description for p n").optional() - val current by parser.option(ArgType.Boolean, shortName = "c", description = "Filter by currently running program") - val lineNumbers by parser.option(ArgType.Boolean, shortName = "ln", description = "Show line numbers") + val packageName by parser.argument( + ArgType.String, + "package name", + "Show logs for a particular application specified by this package name. " + + "Example: 'com.google.android.apps.messaging'." + ).optional() + + val current by parser.option( + ArgType.Boolean, + shortName = "c", + description = "Show logs for an application currently running in foreground" + ) + + val lineNumbers by parser.option( + ArgType.Boolean, + shortName = "ln", + description = "Show line numbers for log lines, embedded into message body" + ) fun validate(args: Array) { parser.parse(args) if (packageName != null && current != null) { - //can't have both at the same time + throw ValidationException("'Package name' and '--current' arguments are mutually exclusive and " + + "can't be used at the same time.") } } }