From 9fdc05ab3c8b07bb3043b2ebcf13864d26760f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Piaggio?= Date: Thu, 26 Oct 2023 10:59:20 -0300 Subject: [PATCH] migrate optics from explore, bump prime styles --- .gitignore | 1 + build.sbt | 9 +-- .../lucuma/ui/input/FormatUtilsSpec.scala | 13 +++-- .../scala/lucuma/ui/optics/OpticsSuite.scala | 37 ++++++++++++ .../main/scala/lucuma/ui/optics/package.scala | 56 +++++++++++++++++++ 5 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 modules/tests/src/test/scala/lucuma/ui/optics/OpticsSuite.scala create mode 100644 modules/ui/src/main/scala/lucuma/ui/optics/package.scala diff --git a/.gitignore b/.gitignore index c165df202..7e3fde455 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ lib_managed/ src_managed/ project/boot/ project/plugins/project/ +sbt-launch.jar # Scala-IDE specific .scala_dependencies diff --git a/build.sbt b/build.sbt index ab207109b..996858b32 100644 --- a/build.sbt +++ b/build.sbt @@ -14,7 +14,7 @@ lazy val kittensVersion = "3.0.0" lazy val http4sVersion = "0.23.23" lazy val http4sDomVersion = "0.2.10" lazy val lucumaCoreVersion = "0.87.0" -lazy val lucumaPrimeStylesVersion = "0.2.9" +lazy val lucumaPrimeStylesVersion = "0.2.10" lazy val lucumaReactVersion = "0.44.1" lazy val lucumaRefinedVersion = "0.1.2" lazy val lucumaSchemasVersion = "0.61.0" @@ -123,9 +123,10 @@ lazy val tests = .dependsOn(testkit) .settings( libraryDependencies ++= Seq( - "edu.gemini" %%% "lucuma-core-testkit" % lucumaCoreVersion % Test, - "org.scalameta" %%% "munit" % "0.7.29" % Test, - "org.typelevel" %%% "discipline-munit" % "1.0.9" % Test + "edu.gemini" %%% "lucuma-core-testkit" % lucumaCoreVersion % Test, + "edu.gemini" %%% "lucuma-schemas-testkit" % lucumaSchemasVersion % Test, + "org.scalameta" %%% "munit" % "0.7.29" % Test, + "org.typelevel" %%% "discipline-munit" % "1.0.9" % Test ) ) .enablePlugins(ScalaJSPlugin, NoPublishPlugin) diff --git a/modules/tests/src/test/scala/lucuma/ui/input/FormatUtilsSpec.scala b/modules/tests/src/test/scala/lucuma/ui/input/FormatUtilsSpec.scala index 26f5aed13..89c1fab02 100644 --- a/modules/tests/src/test/scala/lucuma/ui/input/FormatUtilsSpec.scala +++ b/modules/tests/src/test/scala/lucuma/ui/input/FormatUtilsSpec.scala @@ -1,14 +1,16 @@ // Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA) // For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause +package lucuma.ui.input + import eu.timepit.refined.types.numeric.NonNegInt -import lucuma.ui.input.FormatUtils._ +import lucuma.ui.input.FormatUtils.* import munit.DisciplineSuite -import org.scalacheck.Arbitrary._ -import org.scalacheck.Prop._ -import org.scalacheck._ +import org.scalacheck.Arbitrary.* +import org.scalacheck.Prop.* +import org.scalacheck.* -final class FormatUtilsSpec extends DisciplineSuite { +final class FormatUtilsSpec extends DisciplineSuite: val Zero = BigInt(0) test("stripZerosPastNPlaces") { forAll(arbitrary[BigInt], Gen.choose(0, 999999), Gen.choose(0, 9), Gen.choose(0, 9)) { @@ -19,4 +21,3 @@ final class FormatUtilsSpec extends DisciplineSuite { assertEquals(stripZerosPastNPlaces(s"$i.$d", n), s"$i.$keep") } } -} diff --git a/modules/tests/src/test/scala/lucuma/ui/optics/OpticsSuite.scala b/modules/tests/src/test/scala/lucuma/ui/optics/OpticsSuite.scala new file mode 100644 index 000000000..270cd31f0 --- /dev/null +++ b/modules/tests/src/test/scala/lucuma/ui/optics/OpticsSuite.scala @@ -0,0 +1,37 @@ +// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA) +// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause + +package lucuma.ui.optics + +import cats.kernel.Eq +import lucuma.core.util.arb.ArbEnumerated.given +import lucuma.schemas.model.ObservingMode.GmosSouthLongSlit +import lucuma.schemas.model.arb.ArbObservingMode.given +import monocle.law.discipline.LensTests +import munit.DisciplineSuite +import org.scalacheck.Arbitrary +import org.scalacheck.Arbitrary.* + +class OpticsSuite extends DisciplineSuite: + val disjointZip2 = + disjointZip( + GmosSouthLongSlit.grating, + GmosSouthLongSlit.filter + ) + val disjointZip3 = + disjointZip( + GmosSouthLongSlit.grating, + GmosSouthLongSlit.filter, + GmosSouthLongSlit.fpu + ) + val disjointZip4 = + disjointZip( + GmosSouthLongSlit.grating, + GmosSouthLongSlit.filter, + GmosSouthLongSlit.fpu, + GmosSouthLongSlit.explicitRoi + ) + + checkAll("disjointZip2", LensTests(disjointZip2)) + checkAll("disjointZip3", LensTests(disjointZip3)) + checkAll("disjointZip4", LensTests(disjointZip4)) diff --git a/modules/ui/src/main/scala/lucuma/ui/optics/package.scala b/modules/ui/src/main/scala/lucuma/ui/optics/package.scala new file mode 100644 index 000000000..d6f9c05aa --- /dev/null +++ b/modules/ui/src/main/scala/lucuma/ui/optics/package.scala @@ -0,0 +1,56 @@ +// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA) +// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause + +package lucuma.ui.optics + +import cats.syntax.all.* +import monocle.Lens +import monocle.Iso + +// Lenses must be disjoint (not overlap), or the result will be unsafe. +// See https://github.com/optics-dev/Monocle/issues/545 +def disjointZip[S, A, B](l1: Lens[S, A], l2: Lens[S, B]): Lens[S, (A, B)] = + Lens((s: S) => (l1.get(s), l2.get(s)))((ab: (A, B)) => + (s: S) => l2.replace(ab._2)(l1.replace(ab._1)(s)) + ) + +def disjointZip[S, A, B, C](l1: Lens[S, A], l2: Lens[S, B], l3: Lens[S, C]): Lens[S, (A, B, C)] = + Lens((s: S) => (l1.get(s), l2.get(s), l3.get(s)))((abc: (A, B, C)) => + (s: S) => l3.replace(abc._3)(l2.replace(abc._2)(l1.replace(abc._1)(s))) + ) + +def disjointZip[S, A, B, C, D]( + l1: Lens[S, A], + l2: Lens[S, B], + l3: Lens[S, C], + l4: Lens[S, D] +): Lens[S, (A, B, C, D)] = + Lens((s: S) => (l1.get(s), l2.get(s), l3.get(s), l4.get(s)))((abc: (A, B, C, D)) => + (s: S) => l4.replace(abc._4)(l3.replace(abc._3)(l2.replace(abc._2)(l1.replace(abc._1)(s)))) + ) + +def disjointZip[S, A, B, C, D, E]( + l1: Lens[S, A], + l2: Lens[S, B], + l3: Lens[S, C], + l4: Lens[S, D], + l5: Lens[S, E] +): Lens[S, (A, B, C, D, E)] = + Lens((s: S) => (l1.get(s), l2.get(s), l3.get(s), l4.get(s), l5.get(s)))((abc: (A, B, C, D, E)) => + (s: S) => + l5.replace(abc._5)( + l4.replace(abc._4)(l3.replace(abc._3)(l2.replace(abc._2)(l1.replace(abc._1)(s)))) + ) + ) + +// This only behaves as a lawful lens as long as A and B are both null or both set. +def unsafeDisjointOptionZip[S, A, B]( + l1: Lens[S, Option[A]], + l2: Lens[S, Option[B]] +): Lens[S, Option[(A, B)]] = + Lens((s: S) => (l1.get(s), l2.get(s)).tupled)((ab: Option[(A, B)]) => + (s: S) => l2.replace(ab.map(_._2))(l1.replace(ab.map(_._1))(s)) + ) + +def optionIso[A, B](iso: Iso[A, B]): Iso[Option[A], Option[B]] = + Iso[Option[A], Option[B]](_.map(iso.get))(_.map(iso.reverseGet))