diff --git a/build.sbt b/build.sbt
index 1263136..0637891 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,6 +1,6 @@
import xerial.sbt.Sonatype.sonatypeCentralHost
-ThisBuild / scalaVersion := "3.5.0"
+ThisBuild / scalaVersion := "3.6.2"
ThisBuild / licenses := List(("MIT", url("https://opensource.org/licenses/MIT")))
ThisBuild / homepage := Some(url("https://github.com/d10xa/json-log-viewer"))
ThisBuild / organization := "ru.d10xa"
@@ -17,6 +17,14 @@ ThisBuild / sonatypeCredentialHost := sonatypeCentralHost
val circeVersion = "0.14.10"
val declineVersion = "2.4.1"
val fs2Version = "3.11.0"
+val munitVersion = "1.0.3"
+
+lazy val root = project.in(file(".")).
+ aggregate(`json-log-viewer`.js, `json-log-viewer`.jvm, `frontend-laminar`).
+ settings(
+ publish := {},
+ publishLocal := {},
+ )
lazy val `json-log-viewer` = crossProject(JSPlatform, JVMPlatform)
.in(file("json-log-viewer"))
@@ -28,6 +36,7 @@ lazy val `json-log-viewer` = crossProject(JSPlatform, JVMPlatform)
pomIncludeRepository := { _ => false },
libraryDependencies ++= Seq(
"org.typelevel" %%% "cats-effect" % "3.5.4",
+ "org.typelevel" %%% "munit-cats-effect" % "2.0.0" % Test,
"co.fs2" %%% "fs2-core" % fs2Version,
"co.fs2" %%% "fs2-io" % fs2Version,
"com.monovore" %%% "decline" % declineVersion,
@@ -36,20 +45,22 @@ lazy val `json-log-viewer` = crossProject(JSPlatform, JVMPlatform)
"io.circe" %%% "circe-literal" % circeVersion % Test,
"io.circe" %%% "circe-parser" % circeVersion,
"io.circe" %%% "circe-generic" % circeVersion,
- "io.circe" %%% "circe-yaml-scalayaml" % "0.16.0",
"com.lihaoyi" %%% "fansi" % "0.5.0",
"org.scala-lang.modules" %%% "scala-parser-combinators" % "2.4.0",
- "org.scalameta" %% "munit" % "0.7.29" % Test
+ "org.scalameta" %%% "munit" % munitVersion % Test
),
fork := true,
run / connectInput := true,
)
.jvmSettings(
+ libraryDependencies ++= Seq(
+ "io.circe" %%% "circe-yaml-scalayaml" % "0.16.0"
+ ),
publish / skip := false
)
.jsSettings(
publish / skip := true,
- fork := false,
+ fork := false
)
lazy val `make-logs` = project
@@ -77,12 +88,13 @@ lazy val `frontend-laminar` = project
"com.raquo" %%% "airstream" % "16.0.0",
"com.raquo" %%% "waypoint" % "7.0.0",
"com.github.plokhotnyuk.jsoniter-scala" %%% "jsoniter-scala-core" % "2.28.4",
- "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.28.4" % "provided"
+ "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.28.4" % "provided",
+ "org.scalameta" %% "munit" % munitVersion % Test
),
Compile / fastLinkJS / scalaJSLinkerConfig ~= { _.withSourceMap(true) },
Compile / fullLinkJS / scalaJSLinkerConfig ~= { _.withSourceMap(true) },
scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.CommonJSModule) },
+
scalaJSUseMainModuleInitializer := true,
- (Test / requireJsDomEnv) := true,
- useYarn := true
+ (Test / requireJsDomEnv) := true
)
diff --git a/frontend-laminar/src/main/resources/index-fastopt.html b/frontend-laminar/src/main/resources/index-fastopt.html
index 8295338..6871b8c 100644
--- a/frontend-laminar/src/main/resources/index-fastopt.html
+++ b/frontend-laminar/src/main/resources/index-fastopt.html
@@ -7,7 +7,7 @@
-
+
diff --git a/frontend-laminar/src/main/resources/index-fullopt.html b/frontend-laminar/src/main/resources/index-fullopt.html
index 8e5b39d..a59e18f 100644
--- a/frontend-laminar/src/main/resources/index-fullopt.html
+++ b/frontend-laminar/src/main/resources/index-fullopt.html
@@ -5,7 +5,7 @@
[FULL] json-log-viewer
-
+
diff --git a/frontend-laminar/src/main/scala/ViewElement.scala b/frontend-laminar/src/main/scala/ViewElement.scala
deleted file mode 100644
index 80fc995..0000000
--- a/frontend-laminar/src/main/scala/ViewElement.scala
+++ /dev/null
@@ -1,62 +0,0 @@
-import App.textVar
-import com.monovore.decline.Help
-import com.raquo.airstream.core.Signal
-import com.raquo.laminar.DomApi
-import com.raquo.laminar.api.L.{*, given}
-import com.raquo.laminar.api.L
-import ru.d10xa.jsonlogviewer.Application
-import ru.d10xa.jsonlogviewer.JsonDetector
-import ru.d10xa.jsonlogviewer.JsonPrefixPostfix
-import ru.d10xa.jsonlogviewer.JsonLogLineParser
-import ru.d10xa.jsonlogviewer.TimestampFilter
-import ru.d10xa.jsonlogviewer.LogViewerStream
-import ru.d10xa.jsonlogviewer.LogLineFilter
-import fs2.*
-import ru.d10xa.jsonlogviewer.decline.Config.FormatIn
-import ru.d10xa.jsonlogviewer.decline.Config
-import ru.d10xa.jsonlogviewer.decline.Config
-import ru.d10xa.jsonlogviewer.decline.TimestampConfig
-import ru.d10xa.jsonlogviewer.decline.TimestampConfig
-import ru.d10xa.jsonlogviewer.formatout.ColorLineFormatter
-import ru.d10xa.jsonlogviewer.logfmt.LogfmtLogLineParser
-
-import scala.util.chaining.scalaUtilChainingOps
-object ViewElement {
-
- def runApp(
- logLinesSignal: Signal[String],
- configSignal: Signal[Either[Help, Config]]
- ): Signal[HtmlElement] =
- logLinesSignal.combineWith(configSignal).map {
- case (string, Right(c)) =>
- val jsonPrefixPostfix = JsonPrefixPostfix(JsonDetector())
- val logLineParser = c.formatIn match
- case Some(FormatIn.Logfmt) => LogfmtLogLineParser(c)
- case Some(FormatIn.Json) => JsonLogLineParser(c, jsonPrefixPostfix)
- case other =>
- throw new IllegalStateException(
- s"Unsupported format-in value: $other"
- )
-
- fs2.Stream
- .emits(string.split("\n"))
- .filter(_.trim.nonEmpty)
- .through(LogViewerStream.stream(c, logLineParser))
- .map(Ansi2HtmlWithClasses.apply)
- .toList
- .mkString("", "", "
")
- .pipe(DomApi.unsafeParseHtmlString)
- .pipe(foreignHtmlElement)
- case (string, Left(help)) =>
- pre(cls := "text-light", help.toString)
- }
- def render(
- logLinesSignal: Signal[String],
- configSignal: Signal[Either[Help, Config]]
- ): HtmlElement =
- pre(
- cls := "bg-dark font-monospace text-white",
- child <-- runApp(logLinesSignal, configSignal)
- )
-
-}
diff --git a/frontend-laminar/src/main/scala/Ansi2HtmlWithClasses.scala b/frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/Ansi2HtmlWithClasses.scala
similarity index 96%
rename from frontend-laminar/src/main/scala/Ansi2HtmlWithClasses.scala
rename to frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/Ansi2HtmlWithClasses.scala
index d0701e3..3b71760 100644
--- a/frontend-laminar/src/main/scala/Ansi2HtmlWithClasses.scala
+++ b/frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/Ansi2HtmlWithClasses.scala
@@ -1,3 +1,5 @@
+package ru.d10xa.jsonlogviewer
+
import fansi.ErrorMode
import fansi.Str
@@ -6,7 +8,7 @@ import scala.scalajs.js.annotation.JSExport
/** https://github.com/com-lihaoyi/fansi/issues/62
*/
object Ansi2HtmlWithClasses extends Function1[String, String]:
- private def transition(from: fansi.Attr, to: fansi.Attr) =
+ def transition(from: fansi.Attr, to: fansi.Attr): String =
import fansi.*
(from, to) match
case (Underlined.Off, Underlined.On) => ""
diff --git a/frontend-laminar/src/main/scala/App.scala b/frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/App.scala
similarity index 91%
rename from frontend-laminar/src/main/scala/App.scala
rename to frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/App.scala
index f7dca2d..a46886d 100644
--- a/frontend-laminar/src/main/scala/App.scala
+++ b/frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/App.scala
@@ -1,20 +1,25 @@
-import Router0.*
+package ru.d10xa.jsonlogviewer
+
import com.monovore.decline.Help
-import com.raquo.laminar.DomApi
import com.raquo.laminar.api.L
import com.raquo.laminar.api.L.*
import com.raquo.laminar.nodes.ReactiveHtmlElement
import com.raquo.waypoint.*
-import fansi.ErrorMode
import org.scalajs.dom
import org.scalajs.dom.HTMLButtonElement
import org.scalajs.dom.HTMLDivElement
+import ru.d10xa.jsonlogviewer.Router0.*
+import ru.d10xa.jsonlogviewer.Router0.EditPage
+import ru.d10xa.jsonlogviewer.Router0.HelpPage
+import ru.d10xa.jsonlogviewer.Router0.LivePage
+import ru.d10xa.jsonlogviewer.Router0.Page
+import ru.d10xa.jsonlogviewer.Router0.ViewPage
+import ru.d10xa.jsonlogviewer.Router0.navigateTo
+import ru.d10xa.jsonlogviewer.decline.Config
import ru.d10xa.jsonlogviewer.decline.Config.FormatIn
-import ru.d10xa.jsonlogviewer.decline.Config.FormatOut
import ru.d10xa.jsonlogviewer.decline.Config.FormatIn.Json
import ru.d10xa.jsonlogviewer.decline.Config.FormatIn.Logfmt
-import ru.d10xa.jsonlogviewer.decline.Config
-import ru.d10xa.jsonlogviewer.decline.Config
+import ru.d10xa.jsonlogviewer.decline.Config.FormatOut
import ru.d10xa.jsonlogviewer.decline.DeclineOpts
import ru.d10xa.jsonlogviewer.query.QueryCompiler
@@ -50,7 +55,7 @@ object App {
val formatInVar: Var[FormatIn] = Var(
FormatIn.Json
)
- val formatOutVar: Var[FormatOut] = Var(
+ val formatOutVar: Var[FormatOut] = Var(
FormatOut.Pretty
)
@@ -71,7 +76,13 @@ object App {
case Right(value) => Some(value)
} yield DeclineOpts.command
.parse(splitArgs(cli))
- .map(cfg => cfg.copy(filter = filter, formatIn = Some(formatIn), formatOut = Some(formatOut)))
+ .map(cfg =>
+ cfg.copy(
+ filter = filter,
+ formatIn = Some(formatIn),
+ formatOut = Some(formatOut)
+ )
+ )
def main(args: Array[String]): Unit = {
lazy val container = dom.document.getElementById("app-container")
@@ -135,11 +146,11 @@ object App {
select(
cls := "col-1",
value <-- formatOutVar.signal.map {
- case FormatOut.Raw => "raw"
+ case FormatOut.Raw => "raw"
case FormatOut.Pretty => "pretty"
},
onChange.mapToValue.map {
- case "raw" => FormatOut.Raw
+ case "raw" => FormatOut.Raw
case "pretty" => FormatOut.Pretty
} --> formatOutVar,
option(value := "pretty", "pretty"),
@@ -158,7 +169,7 @@ object App {
onInput.mapToValue --> filterVar
)
)
-)
+ )
def additionalArgsDiv: ReactiveHtmlElement[HTMLDivElement] = div(
cls := "row-fluid",
@@ -193,6 +204,7 @@ object App {
}
)
private def renderLivePage(): HtmlElement = {
+ implicit val owner: Owner = new Owner {}
div(
formatInDiv,
formatOutDiv,
@@ -205,6 +217,7 @@ object App {
}
private def renderViewPage(): HtmlElement = {
+ implicit val owner: Owner = new Owner {}
div(
ViewElement.render(textVar.signal, configSignal)
)
diff --git a/frontend-laminar/src/main/scala/EditElement.scala b/frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/EditElement.scala
similarity index 78%
rename from frontend-laminar/src/main/scala/EditElement.scala
rename to frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/EditElement.scala
index 46a703c..ae9f6df 100644
--- a/frontend-laminar/src/main/scala/EditElement.scala
+++ b/frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/EditElement.scala
@@ -1,5 +1,7 @@
-import com.raquo.laminar.api.L.{*, given}
+package ru.d10xa.jsonlogviewer
+
import com.raquo.laminar.api.L
+import com.raquo.laminar.api.L.*
object EditElement {
def render(mods: Modifier[TextArea]*): HtmlElement =
diff --git a/frontend-laminar/src/main/scala/Router0.scala b/frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/Router0.scala
similarity index 97%
rename from frontend-laminar/src/main/scala/Router0.scala
rename to frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/Router0.scala
index eb17fab..96155dd 100644
--- a/frontend-laminar/src/main/scala/Router0.scala
+++ b/frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/Router0.scala
@@ -1,8 +1,10 @@
-import com.raquo.laminar.api.L.{*, given}
-import com.raquo.waypoint.*
-import org.scalajs.dom
+package ru.d10xa.jsonlogviewer
+
import com.github.plokhotnyuk.jsoniter_scala.core.*
import com.github.plokhotnyuk.jsoniter_scala.macros.*
+import com.raquo.laminar.api.L.*
+import com.raquo.waypoint.*
+import org.scalajs.dom
object Router0 {
diff --git a/frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/ViewElement.scala b/frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/ViewElement.scala
new file mode 100644
index 0000000..2a3a8cc
--- /dev/null
+++ b/frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/ViewElement.scala
@@ -0,0 +1,74 @@
+package ru.d10xa.jsonlogviewer
+
+import cats.effect.IO
+import cats.effect.unsafe.implicits.global
+import com.monovore.decline.Help
+import com.raquo.airstream.core.Signal
+import com.raquo.airstream.eventbus.EventBus
+import com.raquo.airstream.ownership.Owner
+import com.raquo.laminar.DomApi
+import com.raquo.laminar.api.L.*
+import ru.d10xa.jsonlogviewer.decline.Config
+import ru.d10xa.jsonlogviewer.decline.yaml.ConfigYaml
+import ru.d10xa.jsonlogviewer.decline.yaml.Feed
+
+import scala.util.chaining.*
+
+object ViewElement {
+
+ def stringsToHtmlElement(strings: List[String]): HtmlElement =
+ strings
+ .map(Ansi2HtmlWithClasses.apply)
+ .mkString("", "", "
")
+ .pipe(DomApi.unsafeParseHtmlString)
+ .pipe(foreignHtmlElement)
+
+ def modifyConfigForInlineInput(string: String, config: Config): Config =
+ config.copy(configYaml =
+ Some(
+ ConfigYaml(
+ filter = None,
+ formatIn = None,
+ commands = None,
+ feeds = Some(
+ List(
+ Feed(
+ name = None,
+ commands = List.empty,
+ inlineInput = Some(string),
+ filter = config.filter,
+ formatIn = config.formatIn
+ )
+ )
+ )
+ )
+ )
+ )
+
+ def render(
+ logLinesSignal: Signal[String],
+ configSignal: Signal[Either[Help, Config]]
+ )(implicit owner: Owner): HtmlElement = {
+ val eventBus = new EventBus[HtmlElement]
+ logLinesSignal
+ .combineWith(configSignal)
+ .foreach {
+ case (string, Right(c)) =>
+ LogViewerStream
+ .stream(modifyConfigForInlineInput(string, c))
+ .compile
+ .toList
+ .map(stringsToHtmlElement)
+ .flatMap(e => IO(eventBus.writer.onNext(e)))
+ .unsafeRunAndForget()
+
+ case (_, Left(help)) =>
+ eventBus.writer.onNext(pre(cls := "text-light", help.toString))
+ }(owner)
+
+ pre(
+ cls := "bg-dark font-monospace text-white",
+ child <-- eventBus.events
+ )
+ }
+}
diff --git a/frontend-laminar/src/test/scala/ru/d10xa/jsonlogviewer/Ansi2HtmlWithClassesTest.scala b/frontend-laminar/src/test/scala/ru/d10xa/jsonlogviewer/Ansi2HtmlWithClassesTest.scala
new file mode 100644
index 0000000..f32e792
--- /dev/null
+++ b/frontend-laminar/src/test/scala/ru/d10xa/jsonlogviewer/Ansi2HtmlWithClassesTest.scala
@@ -0,0 +1,44 @@
+package ru.d10xa.jsonlogviewer
+
+import fansi.Attr
+import fansi.Bold
+import fansi.Color
+import fansi.Underlined
+import munit.FunSuite
+
+class Ansi2HtmlWithClassesTest extends FunSuite {
+
+ test("transition correctly handles Bold.On to Bold.Off") {
+ val from = Bold.On
+ val to = Bold.Off
+ val result = Ansi2HtmlWithClasses.transition(from, to)
+ assertEquals(result, "")
+ }
+
+ test("transition correctly handles Underlined.Off to Underlined.On") {
+ val from = Underlined.Off
+ val to = Underlined.On
+ val result = Ansi2HtmlWithClasses.transition(from, to)
+ assertEquals(result, "")
+ }
+
+ test("transition returns empty string for unmatched attributes") {
+ val from = Bold.Off
+ val to = Color.Reset
+ val result = Ansi2HtmlWithClasses.transition(from, to)
+ assertEquals(result, "")
+ }
+
+ test("apply correctly handles a simple ANSI string") {
+ val input = "\u001b[1mBold\u001b[0m"
+ val result = Ansi2HtmlWithClasses.apply(input)
+ assert(result.contains("Bold"), "Expected bold tags around 'Bold'")
+ }
+
+ test("apply handles empty string gracefully") {
+ val input = ""
+ val result = Ansi2HtmlWithClasses.apply(input)
+ assertEquals(result, "")
+ }
+
+}
diff --git a/json-log-viewer/js/src/main/scala/ru/d10xa/jsonlogviewer/StdInLinesStreamImpl.scala b/json-log-viewer/js/src/main/scala/ru/d10xa/jsonlogviewer/StdInLinesStreamImpl.scala
new file mode 100644
index 0000000..888bdf4
--- /dev/null
+++ b/json-log-viewer/js/src/main/scala/ru/d10xa/jsonlogviewer/StdInLinesStreamImpl.scala
@@ -0,0 +1,7 @@
+package ru.d10xa.jsonlogviewer
+import cats.effect.IO
+
+class StdInLinesStreamImpl extends StdInLinesStream {
+
+ override def stdinLinesStream: fs2.Stream[IO, String] = fs2.Stream.empty
+}
diff --git a/json-log-viewer/js/src/main/scala/ru/d10xa/jsonlogviewer/decline/ConfigInitImpl.scala b/json-log-viewer/js/src/main/scala/ru/d10xa/jsonlogviewer/decline/ConfigInitImpl.scala
index 6d55988..811d241 100644
--- a/json-log-viewer/js/src/main/scala/ru/d10xa/jsonlogviewer/decline/ConfigInitImpl.scala
+++ b/json-log-viewer/js/src/main/scala/ru/d10xa/jsonlogviewer/decline/ConfigInitImpl.scala
@@ -1,9 +1,5 @@
package ru.d10xa.jsonlogviewer.decline
-import cats.data.Validated
-import cats.data.Validated
-import cats.effect.IO
-import cats.effect.IO
import cats.effect.IO
import ru.d10xa.jsonlogviewer.decline.Config.FormatIn
diff --git a/json-log-viewer/js/src/main/scala/ru/d10xa/jsonlogviewer/shell/ShellImpl.scala b/json-log-viewer/js/src/main/scala/ru/d10xa/jsonlogviewer/shell/ShellImpl.scala
index 553ec5d..c2cce38 100644
--- a/json-log-viewer/js/src/main/scala/ru/d10xa/jsonlogviewer/shell/ShellImpl.scala
+++ b/json-log-viewer/js/src/main/scala/ru/d10xa/jsonlogviewer/shell/ShellImpl.scala
@@ -1,12 +1,16 @@
package ru.d10xa.jsonlogviewer.shell
-import cats.effect.*
+import cats.effect.IO
import fs2.*
-import java.io.*
+class ShellImpl extends Shell {
-class ShellImpl[F[_]] extends Shell[F] {
-
- def mergeCommands(commands: List[String]): Stream[F, String] = Stream.empty
+ def mergeCommandsAndInlineInput(
+ commands: List[String],
+ inlineInput: Option[String]
+ ): Stream[IO, String] = inlineInput match
+ case Some(inlineInput) =>
+ Shell.stringToStream(inlineInput)
+ case None => Stream.empty
}
diff --git a/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/StdInLinesStreamImpl.scala b/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/StdInLinesStreamImpl.scala
new file mode 100644
index 0000000..1593f8b
--- /dev/null
+++ b/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/StdInLinesStreamImpl.scala
@@ -0,0 +1,13 @@
+package ru.d10xa.jsonlogviewer
+import cats.effect.IO
+import fs2.Chunk
+import fs2.Stream
+import fs2.io.stdinUtf8
+
+class StdInLinesStreamImpl extends StdInLinesStream {
+
+ override def stdinLinesStream: Stream[IO, String] =
+ stdinUtf8[IO](1024 * 1024 * 10)
+ .repartition(s => Chunk.array(s.split("\n", -1)))
+ .filter(_.nonEmpty)
+}
diff --git a/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/ConfigInitImpl.scala b/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/ConfigInitImpl.scala
index c240d8e..c28aa13 100644
--- a/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/ConfigInitImpl.scala
+++ b/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/ConfigInitImpl.scala
@@ -2,10 +2,12 @@ package ru.d10xa.jsonlogviewer.decline
import cats.data.Validated
import cats.effect.IO
-import ru.d10xa.jsonlogviewer.ConfigYamlReader
import ru.d10xa.jsonlogviewer.decline.Config.FormatIn
import cats.syntax.all.*
import ru.d10xa.jsonlogviewer.decline.yaml.ConfigYaml
+import ru.d10xa.jsonlogviewer.decline.yaml.ConfigYamlLoader
+import ru.d10xa.jsonlogviewer.decline.yaml.ConfigYamlLoaderImpl
+import ru.d10xa.jsonlogviewer.decline.yaml.ConfigYamlReader
import java.io.File
diff --git a/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoader.scala b/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoader.scala
new file mode 100644
index 0000000..77b93c7
--- /dev/null
+++ b/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoader.scala
@@ -0,0 +1,7 @@
+package ru.d10xa.jsonlogviewer.decline.yaml
+
+import cats.data.ValidatedNel
+
+trait ConfigYamlLoader {
+ def parseYamlFile(content: String): ValidatedNel[String, ConfigYaml]
+}
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoader.scala b/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoaderImpl.scala
similarity index 88%
rename from json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoader.scala
rename to json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoaderImpl.scala
index 086817b..bf08d35 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoader.scala
+++ b/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoaderImpl.scala
@@ -10,10 +10,9 @@ import io.circe.yaml.scalayaml.parser
import ru.d10xa.jsonlogviewer.decline.Config.FormatIn
import ru.d10xa.jsonlogviewer.decline.FormatInValidator
import ru.d10xa.jsonlogviewer.decline.QueryASTValidator
-import ru.d10xa.jsonlogviewer.decline.yaml.ConfigYaml
import ru.d10xa.jsonlogviewer.query.QueryAST
-object ConfigYamlLoader {
+class ConfigYamlLoaderImpl extends ConfigYamlLoader {
private def trimCommentedLines(str: String): String =
str.linesIterator
.filterNot(line =>
@@ -117,22 +116,42 @@ object ConfigYamlLoader {
Validated.invalidNel(s"Missing '$fieldName' field in feed")
}
+ private def parseOptionalString(
+ fields: Map[String, Json],
+ fieldName: String
+ ): ValidatedNel[String, Option[String]] =
+ fields.get(fieldName) match {
+ case Some(c) =>
+ c.as[Option[String]]
+ .leftMap(_ => s"Invalid '$fieldName' field in feed")
+ .toValidatedNel
+ case None =>
+ Validated.valid(None)
+ }
+
private def parseFeed(feedJson: Json): ValidatedNel[String, Feed] =
feedJson.asObject.map(_.toMap) match {
case None => Validated.invalidNel("Feed entry is not a valid JSON object")
case Some(feedFields) =>
- val nameValidated = parseString(
+ val nameValidated = parseOptionalString(
feedFields,
- "name",
- "Invalid 'name' field in feed"
+ "name"
)
val commandsValidated = parseListString(feedFields, "commands")
+ val inlineInputValidated =
+ parseOptionalString(feedFields, "inlineInput")
val filterValidated = parseOptionalQueryAST(feedFields, "filter")
val formatInValidated
: Validated[NonEmptyList[String], Option[FormatIn]] =
parseOptionalFormatIn(feedFields, "formatIn")
- (nameValidated, commandsValidated, filterValidated, formatInValidated)
+ (
+ nameValidated,
+ commandsValidated,
+ inlineInputValidated,
+ filterValidated,
+ formatInValidated
+ )
.mapN(Feed.apply)
}
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/ConfigYamlReader.scala b/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlReader.scala
similarity index 74%
rename from json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/ConfigYamlReader.scala
rename to json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlReader.scala
index 9d91e90..380c46a 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/ConfigYamlReader.scala
+++ b/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlReader.scala
@@ -1,14 +1,14 @@
-package ru.d10xa.jsonlogviewer
+package ru.d10xa.jsonlogviewer.decline.yaml
-import cats.effect.IO
import cats.data.ValidatedNel
+import cats.effect.IO
import ru.d10xa.jsonlogviewer.decline.yaml.ConfigYaml
import ru.d10xa.jsonlogviewer.decline.yaml.ConfigYamlLoader
import scala.io.Source
object ConfigYamlReader {
-
+ val configYamlLoader: ConfigYamlLoader = ConfigYamlLoaderImpl()
def readFile(filePath: String): IO[String] =
IO.blocking(Source.fromFile(filePath))
.bracket { source =>
@@ -18,5 +18,5 @@ object ConfigYamlReader {
}
def fromYamlFile(filePath: String): IO[ValidatedNel[String, ConfigYaml]] =
- readFile(filePath).map(ConfigYamlLoader.parseYamlFile)
+ readFile(filePath).map(configYamlLoader.parseYamlFile)
}
diff --git a/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/shell/ShellImpl.scala b/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/shell/ShellImpl.scala
index 180571d..65be47f 100644
--- a/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/shell/ShellImpl.scala
+++ b/json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/shell/ShellImpl.scala
@@ -3,33 +3,38 @@ package ru.d10xa.jsonlogviewer.shell
import cats.effect.*
import cats.syntax.all.*
-class ShellImpl[F[_]: Async] extends Shell[F] {
+class ShellImpl extends Shell {
- def createProcess(command: String): Resource[F, Process] =
- Resource.make(Async[F].delay {
+ def createProcess(command: String): Resource[IO, Process] =
+ Resource.make(IO {
new ProcessBuilder("sh", "-c", command)
.redirectErrorStream(true)
.start()
- })(process => Async[F].delay(process.destroy()))
+ })(process => IO(process.destroy()))
- def runInfiniteCommand(command: String): fs2.Stream[F, String] =
+ private def runInfiniteCommand(command: String): fs2.Stream[IO, String] =
fs2.Stream.resource(createProcess(command)).flatMap { process =>
fs2.io
.readInputStream(
- Async[F].delay(process.getInputStream),
+ IO(process.getInputStream),
4096,
closeAfterUse = false
)
.through(fs2.text.utf8.decode)
.through(fs2.text.lines)
- .onFinalize(Async[F].delay {
+ .onFinalize(IO {
process.waitFor()
}.void)
}
- def mergeCommands(commands: List[String]): fs2.Stream[F, String] = {
- val streams = commands.map(runInfiniteCommand)
+ def mergeCommandsAndInlineInput(
+ commands: List[String],
+ inlineInput: Option[String]
+ ): fs2.Stream[IO, String] = {
+ val streams =
+ commands
+ .map(runInfiniteCommand) ++ inlineInput.map(Shell.stringToStream).toList
fs2.Stream.emits(streams).parJoin(math.max(1, commands.length))
}
-}
\ No newline at end of file
+}
diff --git a/json-log-viewer/shared/src/test/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoaderTest.scala b/json-log-viewer/jvm/src/test/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoaderTest.scala
similarity index 83%
rename from json-log-viewer/shared/src/test/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoaderTest.scala
rename to json-log-viewer/jvm/src/test/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoaderTest.scala
index 3078b61..3375e79 100644
--- a/json-log-viewer/shared/src/test/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoaderTest.scala
+++ b/json-log-viewer/jvm/src/test/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoaderTest.scala
@@ -3,10 +3,14 @@ package ru.d10xa.jsonlogviewer.decline.yaml
import cats.data.Validated
import munit.FunSuite
import ru.d10xa.jsonlogviewer.decline.Config.FormatIn
+import ru.d10xa.jsonlogviewer.decline.yaml.ConfigYamlLoader
+import ru.d10xa.jsonlogviewer.decline.yaml.ConfigYamlLoaderImpl
import ru.d10xa.jsonlogviewer.query.QueryAST
class ConfigYamlLoaderTest extends FunSuite {
+ private val configYamlLoader: ConfigYamlLoader = new ConfigYamlLoaderImpl
+
test("parse valid yaml with feeds") {
val yaml =
"""|commands:
@@ -31,7 +35,7 @@ class ConfigYamlLoaderTest extends FunSuite {
| formatIn: logfmt
|""".stripMargin
- val result = ConfigYamlLoader.parseYamlFile(yaml)
+ val result = configYamlLoader.parseYamlFile(yaml)
assert(result.isValid, s"Result should be valid: $result")
val config = result.toOption.get
@@ -46,7 +50,7 @@ class ConfigYamlLoaderTest extends FunSuite {
assertEquals(feeds.size, 2)
val feed1 = feeds.head
- assertEquals(feed1.name, "pod-logs")
+ assertEquals(feed1.name, Some("pod-logs"))
assertEquals(
feed1.commands,
List("./mock-logs.sh pod1", "./mock-logs.sh pod2")
@@ -54,14 +58,14 @@ class ConfigYamlLoaderTest extends FunSuite {
assertEquals(feed1.formatIn, Some(FormatIn.Json))
val feed2 = feeds(1)
- assertEquals(feed2.name, "service-logs")
+ assertEquals(feed2.name, Some("service-logs"))
assertEquals(feed2.commands, List("./mock-logs.sh service1"))
assertEquals(feed2.formatIn, Some(FormatIn.Logfmt))
}
test("parse empty yaml") {
val yaml = ""
- val result = ConfigYamlLoader.parseYamlFile(yaml)
+ val result = configYamlLoader.parseYamlFile(yaml)
assert(result.isValid, s"Result should be valid for empty yaml: $result")
val config = result.toOption.get
@@ -76,7 +80,7 @@ class ConfigYamlLoaderTest extends FunSuite {
"""formatIn:
| - not a string
|""".stripMargin
- val result = ConfigYamlLoader.parseYamlFile(yaml)
+ val result = configYamlLoader.parseYamlFile(yaml)
assert(result.isInvalid, s"Result should be invalid: $result")
val errors = result.swap.toOption.get
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/Application.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/Application.scala
index a104752..14e01bb 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/Application.scala
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/Application.scala
@@ -4,15 +4,9 @@ import cats.effect.*
import com.monovore.decline.Opts
import com.monovore.decline.effect.CommandIOApp
import fs2.*
-import fs2.io.*
-import ru.d10xa.jsonlogviewer.decline.Config
-import ru.d10xa.jsonlogviewer.decline.Config.FormatIn
import ru.d10xa.jsonlogviewer.decline.ConfigInit
import ru.d10xa.jsonlogviewer.decline.ConfigInitImpl
import ru.d10xa.jsonlogviewer.decline.DeclineOpts
-import ru.d10xa.jsonlogviewer.decline.yaml.ConfigYaml
-import ru.d10xa.jsonlogviewer.logfmt.LogfmtLogLineParser
-import ru.d10xa.jsonlogviewer.shell.ShellImpl
object Application
extends CommandIOApp(
@@ -26,7 +20,7 @@ object Application
configInit.initConfig(c).flatMap { updatedConfig =>
IO {
LogViewerStream
- .stream[IO](updatedConfig)
+ .stream(updatedConfig)
.through(text.utf8.encode)
.through(io.stdout)
.compile
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/LogViewerStream.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/LogViewerStream.scala
index d75e472..3e7277c 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/LogViewerStream.scala
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/LogViewerStream.scala
@@ -1,9 +1,10 @@
package ru.d10xa.jsonlogviewer
-import cats.effect.Async
+import cats.effect.IO
import cats.syntax.all.*
import fs2.*
import fs2.io.*
+import ru.d10xa.jsonlogviewer.StdInLinesStreamImpl
import ru.d10xa.jsonlogviewer.decline.Config
import ru.d10xa.jsonlogviewer.decline.Config.FormatIn
import ru.d10xa.jsonlogviewer.decline.ConfigInit
@@ -30,24 +31,23 @@ object LogViewerStream {
}
}
- private def commandsToStream[F[_]: Async](
- commands: List[String]
- ): Stream[F, String] = {
- new ShellImpl[F]().mergeCommands(commands)
+ private def commandsAndInlineInputToStream(
+ commands: List[String],
+ inlineInput: Option[String]
+ ): Stream[IO, String] = {
+ new ShellImpl().mergeCommandsAndInlineInput(commands, inlineInput)
}
- private def stdinLinesStream[F[_]: Async]: Stream[F, String] =
- stdinUtf8[F](1024 * 1024 * 10)
- .repartition(s => Chunk.array(s.split("\n", -1)))
- .filter(_.nonEmpty)
+ private val stdinLinesStream: Stream[IO, String] =
+ new StdInLinesStreamImpl().stdinLinesStream
- private def processStream[F[_]: Async](
+ private def processStream(
baseConfig: Config,
- lines: Stream[F, String],
+ lines: Stream[IO, String],
feedFilter: Option[QueryAST],
feedFormatIn: Option[FormatIn],
feedName: Option[String]
- ): Stream[F, String] = {
+ ): Stream[IO, String] = {
val effectiveFormatIn = feedFormatIn.orElse(baseConfig.formatIn)
val effectiveFilter = feedFilter.orElse(baseConfig.filter)
val effectiveConfig = baseConfig.copy(
@@ -65,14 +65,16 @@ object LogViewerStream {
ColorLineFormatter(effectiveConfig, feedName)
lines
- .map(logLineParser.parse)
+ .map { it =>
+ logLineParser.parse(it)
+ }
.filter(logLineFilter.grep)
.filter(logLineFilter.logLineQueryPredicate)
.through(
- timestampFilter.filterTimestampAfter[F](effectiveConfig.timestamp.after)
+ timestampFilter.filterTimestampAfter(effectiveConfig.timestamp.after)
)
.through(
- timestampFilter.filterTimestampBefore[F](
+ timestampFilter.filterTimestampBefore(
effectiveConfig.timestamp.before
)
)
@@ -80,30 +82,34 @@ object LogViewerStream {
.map(_.toString)
}
- def stream[F[_]: Async](config: Config): Stream[F, String] = {
+ def stream(config: Config): Stream[IO, String] = {
val topCommandsOpt: Option[List[String]] =
config.configYaml.flatMap(_.commands).filter(_.nonEmpty)
+
val feedsOpt: Option[List[Feed]] =
config.configYaml.flatMap(_.feeds).filter(_.nonEmpty)
val finalStream = feedsOpt match {
case Some(feeds) =>
val feedStreams = feeds.map { feed =>
- val feedStream = commandsToStream[F](feed.commands)
+ val feedStream: Stream[IO, String] =
+ commandsAndInlineInputToStream(feed.commands, feed.inlineInput)
processStream(
config,
feedStream,
feed.filter,
feed.formatIn,
- feed.name.some
+ feed.name
)
}
Stream.emits(feedStreams).parJoin(feedStreams.size)
case None =>
val baseStream = topCommandsOpt match {
- case Some(cmds) => commandsToStream[F](cmds)
- case None => stdinLinesStream[F]
+ case Some(cmds) =>
+ commandsAndInlineInputToStream(cmds, None)
+ case None =>
+ stdinLinesStream
}
processStream(config, baseStream, None, None, None)
}
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/ParseResultKeys.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/ParseResultKeys.scala
index 8766ccc..d4cbe75 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/ParseResultKeys.scala
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/ParseResultKeys.scala
@@ -6,6 +6,7 @@ import HardcodedFieldNames.loggerNameFieldName
import HardcodedFieldNames.threadNameFieldName
import HardcodedFieldNames.stackTraceFieldName
import ru.d10xa.jsonlogviewer.decline.Config
+
class ParseResultKeys(config: Config) {
def getByKey(
parseResult: ParseResult,
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/StdInLinesStream.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/StdInLinesStream.scala
new file mode 100644
index 0000000..c859967
--- /dev/null
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/StdInLinesStream.scala
@@ -0,0 +1,6 @@
+package ru.d10xa.jsonlogviewer
+import cats.effect.IO
+
+trait StdInLinesStream {
+ def stdinLinesStream: fs2.Stream[IO, String]
+}
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/TimestampFilter.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/TimestampFilter.scala
index c610974..6b8bd5a 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/TimestampFilter.scala
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/TimestampFilter.scala
@@ -1,23 +1,24 @@
package ru.d10xa.jsonlogviewer
+import cats.effect.IO
import fs2.Pipe
import java.time.ZonedDateTime
class TimestampFilter:
- def filterTimestampAfter[F[_]](
+ def filterTimestampAfter(
t: Option[ZonedDateTime]
- ): Pipe[F, ParseResult, ParseResult] = filterTimestamp(t, _.isAfter(_))
+ ): Pipe[IO, ParseResult, ParseResult] = filterTimestamp(t, _.isAfter(_))
- def filterTimestampBefore[F[_]](
+ def filterTimestampBefore(
t: Option[ZonedDateTime]
- ): Pipe[F, ParseResult, ParseResult] = filterTimestamp(t, _.isBefore(_))
+ ): Pipe[IO, ParseResult, ParseResult] = filterTimestamp(t, _.isBefore(_))
- def filterTimestamp[F[_]](
+ def filterTimestamp(
t: Option[ZonedDateTime],
predicate: (ZonedDateTime, ZonedDateTime) => Boolean
- ): Pipe[F, ParseResult, ParseResult] =
+ ): Pipe[IO, ParseResult, ParseResult] =
p =>
t match
case Some(valueFromRequest) =>
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/Config.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/Config.scala
index 3d10b44..2fecfc7 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/Config.scala
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/Config.scala
@@ -1,9 +1,6 @@
package ru.d10xa.jsonlogviewer.decline
-import ru.d10xa.jsonlogviewer.decline.Config
import ru.d10xa.jsonlogviewer.decline.Config.ConfigGrep
-import ru.d10xa.jsonlogviewer.decline.ConfigFile
-import ru.d10xa.jsonlogviewer.decline.TimestampConfig
import ru.d10xa.jsonlogviewer.decline.yaml.ConfigYaml
import ru.d10xa.jsonlogviewer.query.QueryAST
@@ -16,7 +13,7 @@ final case class Config(
grep: List[ConfigGrep],
filter: Option[QueryAST],
formatIn: Option[Config.FormatIn],
- formatOut: Option[Config.FormatOut],
+ formatOut: Option[Config.FormatOut]
)
object Config:
@@ -24,7 +21,7 @@ object Config:
enum FormatIn:
case Json, Logfmt
-
+
enum FormatOut:
case Pretty, Raw
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/DeclineOpts.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/DeclineOpts.scala
index 76b5ab4..03f7a0b 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/DeclineOpts.scala
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/DeclineOpts.scala
@@ -11,7 +11,6 @@ import ru.d10xa.jsonlogviewer.decline.Config.ConfigGrep
import ru.d10xa.jsonlogviewer.decline.Config.FormatIn
import ru.d10xa.jsonlogviewer.decline.Config.FormatOut
import ru.d10xa.jsonlogviewer.query.QueryAST
-import ru.d10xa.jsonlogviewer.query.QueryCompiler
import java.time.ZonedDateTime
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/FormatInValidator.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/FormatInValidator.scala
index 808c56b..de33361 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/FormatInValidator.scala
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/FormatInValidator.scala
@@ -2,7 +2,6 @@ package ru.d10xa.jsonlogviewer.decline
import cats.data.NonEmptyList
import cats.data.Validated
-import cats.data.ValidatedNel
import ru.d10xa.jsonlogviewer.decline.Config.FormatIn
object FormatInValidator {
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/FormatOutValidator.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/FormatOutValidator.scala
index c86966b..f6dcf7d 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/FormatOutValidator.scala
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/FormatOutValidator.scala
@@ -2,15 +2,13 @@ package ru.d10xa.jsonlogviewer.decline
import cats.data.NonEmptyList
import cats.data.Validated
-import cats.data.ValidatedNel
-import ru.d10xa.jsonlogviewer.decline.Config.FormatIn
import ru.d10xa.jsonlogviewer.decline.Config.FormatOut
object FormatOutValidator {
def toValidatedFormatOut(
str: String
): Validated[NonEmptyList[String], FormatOut] = str match
- case "pretty" => Validated.valid(FormatOut.Pretty)
- case "raw" => Validated.valid(FormatOut.Raw)
+ case "pretty" => Validated.valid(FormatOut.Pretty)
+ case "raw" => Validated.valid(FormatOut.Raw)
case other => Validated.invalidNel(s"Wrong format: $other")
}
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYaml.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYaml.scala
index 9e717cf..c869096 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYaml.scala
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYaml.scala
@@ -1,7 +1,6 @@
package ru.d10xa.jsonlogviewer.decline.yaml
import ru.d10xa.jsonlogviewer.decline.Config
-import ru.d10xa.jsonlogviewer.decline.yaml.ConfigYaml
import ru.d10xa.jsonlogviewer.query.QueryAST
case class ConfigYaml(
@@ -13,4 +12,3 @@ case class ConfigYaml(
object ConfigYaml:
val empty: ConfigYaml = ConfigYaml(None, None, None, None)
-
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/Feed.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/Feed.scala
index b61de99..292d1de 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/Feed.scala
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/Feed.scala
@@ -4,8 +4,9 @@ import ru.d10xa.jsonlogviewer.decline.Config.FormatIn
import ru.d10xa.jsonlogviewer.query.QueryAST
case class Feed(
- name: String,
+ name: Option[String],
commands: List[String],
+ inlineInput: Option[String],
filter: Option[QueryAST],
formatIn: Option[FormatIn]
)
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/formatout/ColorLineFormatter.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/formatout/ColorLineFormatter.scala
index 0634ca0..efedaf6 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/formatout/ColorLineFormatter.scala
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/formatout/ColorLineFormatter.scala
@@ -7,7 +7,8 @@ import ru.d10xa.jsonlogviewer.OutputLineFormatter
import ru.d10xa.jsonlogviewer.ParseResult
import ru.d10xa.jsonlogviewer.decline.Config
-class ColorLineFormatter(c: Config, feedName: Option[String]) extends OutputLineFormatter:
+class ColorLineFormatter(c: Config, feedName: Option[String])
+ extends OutputLineFormatter:
private val strEmpty: Str = Str("")
private val strSpace: Str = Str(" ")
private val strNewLine: Str = Str("\n")
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/formatout/RawFormatter.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/formatout/RawFormatter.scala
index 3ed4fe6..e21cef6 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/formatout/RawFormatter.scala
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/formatout/RawFormatter.scala
@@ -1,11 +1,8 @@
package ru.d10xa.jsonlogviewer.formatout
-import fansi.ErrorMode.Strip
-import fansi.EscapeAttr
import fansi.Str
import ru.d10xa.jsonlogviewer.OutputLineFormatter
import ru.d10xa.jsonlogviewer.ParseResult
-import ru.d10xa.jsonlogviewer.decline.Config
class RawFormatter extends OutputLineFormatter:
override def formatLine(p: ParseResult): Str =
diff --git a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/shell/Shell.scala b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/shell/Shell.scala
index 928f3ec..5ba0a9d 100644
--- a/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/shell/Shell.scala
+++ b/json-log-viewer/shared/src/main/scala/ru/d10xa/jsonlogviewer/shell/Shell.scala
@@ -1,7 +1,16 @@
package ru.d10xa.jsonlogviewer.shell
+import cats.effect.IO
import fs2.*
-import cats.effect.*
-trait Shell[F[_]] {
- def mergeCommands(commands: List[String]): Stream[F, String]
+trait Shell {
+ def mergeCommandsAndInlineInput(
+ commands: List[String],
+ inlineInput: Option[String]
+ ): Stream[IO, String]
}
+
+object Shell:
+ def stringToStream(str: String): fs2.Stream[IO, String] =
+ val strings = str.split("\n").filter(_.nonEmpty)
+ fs2.Stream.emits(strings)
+end Shell
diff --git a/json-log-viewer/shared/src/test/scala/ru/d10xa/jsonlogviewer/TimestampFilterTest.scala b/json-log-viewer/shared/src/test/scala/ru/d10xa/jsonlogviewer/TimestampFilterTest.scala
index e1fe7b2..47d9b7b 100644
--- a/json-log-viewer/shared/src/test/scala/ru/d10xa/jsonlogviewer/TimestampFilterTest.scala
+++ b/json-log-viewer/shared/src/test/scala/ru/d10xa/jsonlogviewer/TimestampFilterTest.scala
@@ -1,24 +1,26 @@
package ru.d10xa.jsonlogviewer
+import cats.effect.IO
import fs2.Stream
-import munit.FunSuite
+import munit.CatsEffectSuite
import java.time.ZonedDateTime
-class TimestampFilterTest extends FunSuite {
+class TimestampFilterTest extends CatsEffectSuite {
test("filterTimestampAfter") {
val filter = TimestampFilter()
val t1 = pr("2023-09-17T19:10:01.132318Z")
val t2 = pr("2023-09-19T19:10:03.132318Z")
val stream = Stream.emits(Seq(t1, t2))
- val list = stream
+ stream
.through(
filter.filterTimestampAfter(
Some(ZonedDateTime.parse("2023-09-18T19:10:02Z"))
)
)
+ .compile
.toList
- assertEquals(list, List(t2))
+ .flatTap(list => IO(assertEquals(list, List(t2))))
}
def pr(ts: String): ParseResult = ParseResult(