From 60c59e8d67cec306009c7d81eca4b0bc318e1f0b Mon Sep 17 00:00:00 2001 From: Marius Kavoliunas Date: Thu, 21 Mar 2024 01:40:37 +0200 Subject: [PATCH] make report be printed in a single print command, refactor printing code --- .../org/jmotor/sbt/out/UpdatesPrinter.scala | 85 +++++++++---------- .../jmotor/sbt/out/UpdatesPrinterSpec.scala | 63 +++++++++++++- 2 files changed, 100 insertions(+), 48 deletions(-) diff --git a/src/main/scala/org/jmotor/sbt/out/UpdatesPrinter.scala b/src/main/scala/org/jmotor/sbt/out/UpdatesPrinter.scala index 4b121c9..f0869b3 100644 --- a/src/main/scala/org/jmotor/sbt/out/UpdatesPrinter.scala +++ b/src/main/scala/org/jmotor/sbt/out/UpdatesPrinter.scala @@ -1,10 +1,11 @@ package org.jmotor.sbt.out -import fansi.Color._ -import org.apache.ivy.util.StringUtils -import org.jmotor.sbt.dto.Status._ +import fansi.Color.* +import org.jmotor.sbt.dto.Status.* import org.jmotor.sbt.dto.{ModuleStatus, Status} +import scala.util.Properties.lineSeparator as br + /** * Component: Description: Date: 2016/12/24 * @@ -13,61 +14,55 @@ import org.jmotor.sbt.dto.{ModuleStatus, Status} */ object UpdatesPrinter { - def printStatus(module: ModuleStatus): Unit = { - val status = module.status - lazy val errorMessages = - if (module.errors.nonEmpty) s"\n${module.errors.mkString("\n")}\n" else "updates error, please retry!" - val (color, message) = status match { - case Expired => Yellow -> s"${Blue("--->")} ${Red(module.lastVersion)}" - case Unreleased => Yellow -> s"${Blue("--->")} ${Red(module.lastVersion)}" - case Success => Green -> Green("√") - case Error => Red -> errorMessages - case NotFound => Red -> Red("×") - case s => Red -> Red(s"unknown status ${s.toString}") - } - val length = Status.values.foldLeft(0) { (l, s) => - val length = s.toString.length - if (length > l) { - length - } else { - l - } - } - val level = status.toString + StringUtils.repeat(" ", length - status.toString.length) - print(s"[${color(level)}] ${module.raw} $message \n") - } - def printReporter( projectId: String, plugins: Seq[ModuleStatus], globalPlugins: Seq[ModuleStatus], dependencies: Seq[ModuleStatus] ): Unit = { - import fansi._ + import fansi.* val style = Color.LightBlue ++ Reversed.On ++ Bold.On val titleWidth = 80 val projectTitleWidth = 100 val separator = style(wrap(projectId, " ", projectTitleWidth)) - print(s"$separator \n") - if (globalPlugins.nonEmpty) { - print(s"[info] ${wrap("Global Plugins", "-", titleWidth)}\n") - globalPlugins.foreach(printStatus) - } - if (plugins.nonEmpty) { - print(s"[info] ${wrap(" Plugins", "-", titleWidth)}\n") - plugins.foreach(printStatus) - } - if (dependencies.nonEmpty) { - print(s"[info] ${wrap("Dependencies", "-", titleWidth)}\n") - dependencies.foreach(printStatus) - } + + val setsWithLabels = Seq( + "Global Plugins" -> globalPlugins, + "Plugins" -> plugins, + "Dependencies" -> dependencies + ) + + val report = setsWithLabels.foldLeft(separator)((out, data) => + if (data._2.nonEmpty) + s"""|$out + |[info] ${wrap(data._1, "-", titleWidth)} + |${data._2.map(statusLine).mkString(br)}""".stripMargin + else out + ) + + println(report) } private[out] def wrap(content: String, wrapWith: String, width: Int): String = { - val wrapLength = (width - content.length) / 2 - val range = 0 to wrapLength - val wrapStr = range.map(_ => wrapWith).mkString - s"$wrapStr $content $wrapStr" + val spacedContent = s" $content " + val contentLength = spacedContent.length + (wrapWith * width).patch((width - contentLength) / 2, spacedContent, contentLength) + } + + def statusLine(module: ModuleStatus): String = { + val status = module.status + lazy val errorMessages = + if (module.errors.nonEmpty) s"$br${module.errors.mkString(br)}$br" else "updates error, please retry!" + val (color, message) = status match { + case Expired | Unreleased => Yellow -> s"${Blue("--->")} ${Red(module.lastVersion)}" + case Success => Green -> Green("√") + case Error => Red -> errorMessages + case NotFound => Red -> Red("×") + case s => Red -> Red(s"unknown status ${s.toString}") + } + val padding = Status.values.map(_.toString.length).max - status.toString.length + val level = s"$status${" " * padding}" + s"[${color(level)}] ${module.raw} $message" } } diff --git a/src/test/scala/org/jmotor/sbt/out/UpdatesPrinterSpec.scala b/src/test/scala/org/jmotor/sbt/out/UpdatesPrinterSpec.scala index ffcb692..61e489b 100644 --- a/src/test/scala/org/jmotor/sbt/out/UpdatesPrinterSpec.scala +++ b/src/test/scala/org/jmotor/sbt/out/UpdatesPrinterSpec.scala @@ -1,21 +1,78 @@ package org.jmotor.sbt.out -import org.jmotor.sbt.out.UpdatesPrinter.wrap +import sbt.* +import org.jmotor.sbt.dto.{ModuleStatus, Status} +import org.jmotor.sbt.out.UpdatesPrinter.* import org.scalatest.funsuite.AnyFunSuite +import org.scalatest.matchers.should.Matchers + +import java.io.{ByteArrayOutputStream, PrintStream} +import java.nio.charset.StandardCharsets /** Component: Description: Date: 2018/2/26 * * @author * AI */ -class UpdatesPrinterSpec extends AnyFunSuite { +class UpdatesPrinterSpec extends AnyFunSuite with Matchers{ + + test("print report") { + val consoleOut = captureConsoleOut( + printReporter( + "mainModule", + Seq( + ModuleStatus("oraganization" % "name" % "0.0.1", Status.Success), + ModuleStatus("oraganization" % "name" % "0.0.1", Status.Unreleased, "1.0.0") + ), + Seq(ModuleStatus("oraganization" % "name" % "0.0.1", Status.Expired, "1.0.0")), + Seq(ModuleStatus("oraganization" % "name" % "0.0.1", Status.Unreleased, "1.0.0")) + ) + ) + + consoleOut.uncolor shouldBe + s"""| mainModule${" "} + |[info] -------------------------------- Global Plugins -------------------------------- + |[expired ] oraganization:name:0.0.1 ---> 1.0.0 + |[info] ----------------------------------- Plugins ------------------------------------ + |[success ] oraganization:name:0.0.1 √ + |[unreleased] oraganization:name:0.0.1 ---> 1.0.0 + |[info] --------------------------------- Dependencies --------------------------------- + |[unreleased] oraganization:name:0.0.1 ---> 1.0.0 + |""".stripMargin + } test("print layout") { val width = 80 val t1 = s"[info] ${wrap("Global Plugins", "-", width)}" - val t2 = s"[info] ${wrap(" Plugins", "-", width)}" + val t2 = s"[info] ${wrap("Plugins", "-", width)}" val t3 = s"[info] ${wrap("Dependencies", "-", width)}" assert(t1.length == t2.length && t2.length == t3.length) } + test("status line") { + val t1 = statusLine(ModuleStatus("oraganization" % "name" % "0.0.1", Status.Success)) + val t2 = statusLine(ModuleStatus("oraganization" % "name" % "0.0.1", Status.Expired, "1.0.0")) + val t3 = statusLine(ModuleStatus("oraganization" % "name" % "0.0.1", Status.Error)) + val t4 = statusLine(ModuleStatus("oraganization" % "name" % "0.0.1", Status.Unreleased, "1.0.0")) + + t1.uncolor shouldBe "[success ] oraganization:name:0.0.1 √" + t2.uncolor shouldBe "[expired ] oraganization:name:0.0.1 ---> 1.0.0" + t3.uncolor shouldBe "[error ] oraganization:name:0.0.1 updates error, please retry!" + t4.uncolor shouldBe "[unreleased] oraganization:name:0.0.1 ---> 1.0.0" + } + + def captureConsoleOut(f: => Any): String = { + val outBuffer = new ByteArrayOutputStream() + val interceptionStream = new PrintStream(outBuffer, false, StandardCharsets.UTF_8) + + scala.Console.withOut(interceptionStream)(f) + + val output = new String(outBuffer.toByteArray, StandardCharsets.UTF_8) + output + } + + implicit class StringImplicits(s: String) { + def uncolor: String = s.replaceAll("\u001B\\[[;\\d]*m", "") + } + }