diff --git a/.scalafix.conf b/.scalafix.conf index 25dc9178..c455dd5f 100644 --- a/.scalafix.conf +++ b/.scalafix.conf @@ -1,3 +1,4 @@ include ".scalafix-common.conf" OrganizeImports.removeUnused = false +OrganizeImports.targetDialect = Scala3 \ No newline at end of file diff --git a/modules/ags/src/main/scala/lucuma/ags/GuideStarCandidate.scala b/modules/ags/src/main/scala/lucuma/ags/GuideStarCandidate.scala index 507e436c..bd9d47a5 100644 --- a/modules/ags/src/main/scala/lucuma/ags/GuideStarCandidate.scala +++ b/modules/ags/src/main/scala/lucuma/ags/GuideStarCandidate.scala @@ -10,7 +10,6 @@ import coulomb.* import coulomb.syntax.* import eu.timepit.refined.* import eu.timepit.refined.cats.* -import eu.timepit.refined.collection.NonEmpty import eu.timepit.refined.types.string.NonEmptyString import lucuma.catalog.BandsList import lucuma.core.enums.Band @@ -43,8 +42,7 @@ case class GuideStarCandidate private ( gBrightness: Option[(Band, BrightnessValue)] ) derives Eq { - def name: NonEmptyString = - refineV[NonEmpty](s"Gaia DR3 $id").getOrElse(sys.error("Cannot happen")) + def name: NonEmptyString = GuideStarName.gaiaSourceId.reverseGet(id).toNonEmptyString // Reset the candidate to a given instant // This can be used to calculate and cache the location base on proper motion @@ -79,8 +77,6 @@ object GuideStarCandidate { val UTC = ZoneId.of("UTC") - val GaiaNameRegex = """Gaia DR3 (-?\d+)""".r - val id: Lens[GuideStarCandidate, Long] = Focus[GuideStarCandidate](_.id) @@ -103,10 +99,7 @@ object GuideStarCandidate { .map { case (b, v) => (b, v.value) } new GuideStarCandidate( - st.name.value match { - case GaiaNameRegex(d) => d.toLong - case _ => -1 - }, + GuideStarName.from(st.name.value).toOption.flatMap(_.toGaiaSourceId).getOrElse(-1), st.tracking, gBrightness ) diff --git a/modules/ags/src/main/scala/lucuma/ags/GuideStarName.scala b/modules/ags/src/main/scala/lucuma/ags/GuideStarName.scala new file mode 100644 index 00000000..4201856b --- /dev/null +++ b/modules/ags/src/main/scala/lucuma/ags/GuideStarName.scala @@ -0,0 +1,30 @@ +// 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.ags + +import eu.timepit.refined.string.MatchesRegex +import eu.timepit.refined.types.string.NonEmptyString +import lucuma.core.util.RefinedNewType +import monocle.Prism + +import scala.util.matching.Regex + +// For now, at least, we only support Gaia. +// The same regex used in the odb for a database check. When changing one, both should be updated. +private val regexStr = """^Gaia DR3 (-?\d+)$""" +type GuideStarNamePred = MatchesRegex[regexStr.type] + +object GuideStarName extends RefinedNewType[String, GuideStarNamePred]: + def gaiaSourceId: Prism[GuideStarName, Long] = + Prism[GuideStarName, Long](_.toGaiaSourceId)(id => GuideStarName.unsafeFrom(s"Gaia DR3 $id")) + + extension (self: GuideStarName) + def toGaiaSourceId: Option[Long] = + val regex = Regex(regexStr) + self.value.value match + case regex(d) => d.toLongOption + case _ => None + def toNonEmptyString: NonEmptyString = NonEmptyString.unsafeFrom(self.value.value) + +type GuideStarName = GuideStarName.Type diff --git a/modules/testkit/src/main/scala/lucuma/ags/arb/ArbGuideStarName.scala b/modules/testkit/src/main/scala/lucuma/ags/arb/ArbGuideStarName.scala new file mode 100644 index 00000000..ec48592b --- /dev/null +++ b/modules/testkit/src/main/scala/lucuma/ags/arb/ArbGuideStarName.scala @@ -0,0 +1,27 @@ +// 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.ags.arb + +import lucuma.ags.GuideStarName +import org.scalacheck.Arbitrary +import org.scalacheck.Cogen +import org.scalacheck.Gen + +trait ArbGuideStarName: + + given Arbitrary[GuideStarName] = + Arbitrary { + Gen + .frequency( + // Get the occasional name with too many digits + 1 -> s"9${Long.MaxValue}", + 20 -> Arbitrary.arbLong.arbitrary + ) + .map(a => GuideStarName.unsafeFrom(s"Gaia DR3 $a")) + } + + given Cogen[GuideStarName] = + Cogen[String].contramap(_.value.value) + +object ArbGuideStarName extends ArbGuideStarName diff --git a/modules/tests/shared/src/test/scala/lucuma/ags/GuideStarNameSuite.scala b/modules/tests/shared/src/test/scala/lucuma/ags/GuideStarNameSuite.scala new file mode 100644 index 00000000..a68507cc --- /dev/null +++ b/modules/tests/shared/src/test/scala/lucuma/ags/GuideStarNameSuite.scala @@ -0,0 +1,24 @@ +// 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.ags + +import cats.kernel.Eq +import cats.kernel.laws.discipline.EqTests +import eu.timepit.refined.cats.* +import io.circe.* +import io.circe.refined.* +import io.circe.testing.CodecTests +import io.circe.testing.instances.arbitraryJson +import lucuma.ags.arb.ArbGuideStarName.given +import monocle.law.discipline.PrismTests + +class GuideStarNameSuite extends munit.DisciplineSuite { + test("typeclasses") { + checkAll("GuideStarName", EqTests[GuideStarName].eqv) + checkAll("GuideStarNameCodec", CodecTests[GuideStarName].codec) + checkAll("GuideStarName.from", PrismTests(GuideStarName.from)) + checkAll("GuideStarName.gaiaSourceId", PrismTests(GuideStarName.gaiaSourceId)) + } + +}