diff --git a/modules/core/shared/src/main/scala/lucuma/core/enums/PartnerLinkType.scala b/modules/core/shared/src/main/scala/lucuma/core/enums/PartnerLinkType.scala new file mode 100644 index 00000000..a267e958 --- /dev/null +++ b/modules/core/shared/src/main/scala/lucuma/core/enums/PartnerLinkType.scala @@ -0,0 +1,14 @@ +// 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.core.enums + +import lucuma.core.util.Enumerated + +enum PartnerLinkType(val tag: String) derives Enumerated: + + case HasPartner extends PartnerLinkType("has_partner") + case HasNonPartner extends PartnerLinkType("has_non_partner") + case HasUnspecifiedPartner extends PartnerLinkType("has_unspecified_partner") + +end PartnerLinkType diff --git a/modules/core/shared/src/main/scala/lucuma/core/enums/ProgramUserRole.scala b/modules/core/shared/src/main/scala/lucuma/core/enums/ProgramUserRole.scala new file mode 100644 index 00000000..55315865 --- /dev/null +++ b/modules/core/shared/src/main/scala/lucuma/core/enums/ProgramUserRole.scala @@ -0,0 +1,15 @@ +// 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.core.enums + +import lucuma.core.util.Enumerated + +enum ProgramUserRole(val tag: String) derives Enumerated: + + case Pi extends ProgramUserRole("pi") + case Coi extends ProgramUserRole("coi") + case CoiRO extends ProgramUserRole("coi_ro") + case Support extends ProgramUserRole("support") + +end ProgramUserRole \ No newline at end of file diff --git a/modules/core/shared/src/main/scala/lucuma/core/model/PartnerLink.scala b/modules/core/shared/src/main/scala/lucuma/core/model/PartnerLink.scala new file mode 100644 index 00000000..1ea667bd --- /dev/null +++ b/modules/core/shared/src/main/scala/lucuma/core/model/PartnerLink.scala @@ -0,0 +1,102 @@ +// 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.core.model + + + +import cats.Eq +import cats.syntax.either.* +import lucuma.core.enums.Partner +import lucuma.core.enums.PartnerLinkType +import monocle.Iso + + +/** + * Embodies the association between a user and a partner, if any. There are + * three possible values: `HasPartner` (meaning the user is tied to a particular + * `Partner`), `HasNonPartner` (the user is explicitly not associated with any + * `Partner`) and `HasUnspecifiedPartner` (the relationship is not yet + * determined). + */ +sealed trait PartnerLink extends Product with Serializable: + + def linkType: PartnerLinkType = + fold( + PartnerLinkType.HasUnspecifiedPartner, + PartnerLinkType.HasNonPartner, + _ => PartnerLinkType.HasPartner + ) + + /** + * Converts to an either where Left(true) is `HasNonPartner`, Left(false) is + * `HasUnspecifiedPartner` and Right(partner) is `HasPartner`. + */ + def toEither: Either[Boolean, Partner] = + fold(false.asLeft, true.asLeft, _.asRight) + + /** + * Creates an Option[Partner] which is Some when this link is `HasPartner`. + */ + def partnerOption: Option[Partner] = + toEither.toOption + + /** + * True only if HasNonPartner. + */ + def isNonPartner: Boolean = + fold(false, true, _ => false) + + /** + * True if HasPartner or HasNonPartner. + */ + def isSet: Boolean = + fold(false, true, _ => true) + + /** + * True only if HasUnspecifiedPartner. Same as `!isSet`. + */ + def isUnspecified: Boolean = + !isSet + + def fold[A](unspecified: => A, hasNonPartner: => A, hasPartner: Partner => A): A = + this match { + case PartnerLink.HasUnspecifiedPartner => unspecified + case PartnerLink.HasNonPartner => hasNonPartner + case PartnerLink.HasPartner(p) => hasPartner(p) + } +end PartnerLink + +object PartnerLink: + + case class HasPartner(partner: Partner) extends PartnerLink + case object HasNonPartner extends PartnerLink + case object HasUnspecifiedPartner extends PartnerLink + + given Eq[PartnerLink] = + Eq.by(_.toEither) + + private def fromEither(e: Either[Boolean, Partner]): PartnerLink = + e.fold(b => if (b) HasNonPartner else HasUnspecifiedPartner, HasPartner.apply) + + /** + * Iso for an Either where Left(true) is HasNoPartner and Left(false) is + * HasUnspecifiedPartner. + */ + val either: Iso[PartnerLink, Either[Boolean, Partner]] = + Iso[PartnerLink, Either[Boolean, Partner]](_.toEither)(fromEither) + + /** + * Creates a `PartnerLink` where a None value for `Partner` is interpreted as + * `HasNonPartner`. + */ + def asNonPartner(p: Option[Partner]): PartnerLink = + p.fold(HasNonPartner)(HasPartner.apply) + + def fromLinkType(linkType: PartnerLinkType, partner: Option[Partner] = None): Either[String, PartnerLink] = + linkType match + case PartnerLinkType.HasPartner => partner.map(HasPartner.apply).toRight("HasPartner instance missing partner") + case PartnerLinkType.HasNonPartner => HasNonPartner.asRight + case PartnerLinkType.HasUnspecifiedPartner => HasUnspecifiedPartner.asRight + +end PartnerLink \ No newline at end of file diff --git a/modules/testkit/src/main/scala/lucuma/core/model/arb/ArbPartnerLink.scala b/modules/testkit/src/main/scala/lucuma/core/model/arb/ArbPartnerLink.scala new file mode 100644 index 00000000..8a802265 --- /dev/null +++ b/modules/testkit/src/main/scala/lucuma/core/model/arb/ArbPartnerLink.scala @@ -0,0 +1,44 @@ +// 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.core.model +package arb + +import lucuma.core.enums.Partner +import lucuma.core.enums.PartnerLinkType +import lucuma.core.util.arb.ArbEnumerated +import org.scalacheck.* +import org.scalacheck.Arbitrary.arbitrary +import org.scalacheck.Gen.const + + +trait ArbPartnerLink { + + import ArbEnumerated.given + + given Arbitrary[PartnerLink.HasPartner] = + Arbitrary { + arbitrary[Partner].map(PartnerLink.HasPartner.apply) + } + + given Cogen[PartnerLink.HasPartner] = + Cogen[Partner].contramap(_.partner) + + given Arbitrary[PartnerLink] = + Arbitrary { + Gen.oneOf( + arbitrary[PartnerLink.HasPartner], + const(PartnerLink.HasNonPartner), + const(PartnerLink.HasUnspecifiedPartner) + ) + } + + given Cogen[PartnerLink] = + Cogen[(PartnerLinkType, Option[Partner])].contramap { a => ( + a.linkType, + a.partnerOption + )} + +} + +object ArbPartnerLink extends ArbPartnerLink \ No newline at end of file diff --git a/modules/tests/shared/src/test/scala/lucuma/core/model/PartnerLinkSuite.scala b/modules/tests/shared/src/test/scala/lucuma/core/model/PartnerLinkSuite.scala new file mode 100644 index 00000000..140aca9b --- /dev/null +++ b/modules/tests/shared/src/test/scala/lucuma/core/model/PartnerLinkSuite.scala @@ -0,0 +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.core.model + +import cats.kernel.laws.discipline.EqTests +import lucuma.core.model.arb.ArbPartnerLink +import munit.* + +final class PartnerLinkSuite extends DisciplineSuite: + + import ArbPartnerLink.given + + checkAll("PartnerLink", EqTests[PartnerLink].eqv) + +end PartnerLinkSuite \ No newline at end of file