All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sec.id.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2020 Scala EventStoreDB Client
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package sec

import cats.Eq
import cats.syntax.all.*
import sec.utilities.{guardNonEmpty, guardNotStartsWith}

//======================================================================================================================

/** Stream identifier for streams in EventStoreDB. There are three variants:
  *
  *   - [[StreamId.System]] identifier used for reserverd internal system streams.
  *   - [[StreamId.Normal]] identifier used by users.
  *   - [[StreamId.MetaId]] identifier used for metadata streams of [[StreamId.System]] streams or [[StreamId.Normal]]
  *     streams.
  */
sealed trait StreamId
object StreamId:

  sealed trait Id extends StreamId
  final case class MetaId(id: Id) extends StreamId

  sealed abstract case class System(name: String) extends Id
  private[sec] object System:
    def unsafe(name: String): System = new System(name) {}

  sealed abstract case class Normal(name: String) extends Id
  private[sec] object Normal:
    def unsafe(name: String): Normal = new Normal(name) {}

  //

  val All: System       = System.unsafe("all")
  val Settings: System  = System.unsafe("settings")
  val Stats: System     = System.unsafe("stats")
  val Scavenges: System = System.unsafe("scavenges")
  val Streams: System   = System.unsafe("streams")

  /** @param name
    *   Constructs a stream identifier for a stream. Provided value is validated for non-empty and not starting with the
    *   system reserved metadata prefix `$$`.
    */
  def apply(name: String): Either[InvalidInput, Id] =
    (guardNonEmptyName(name) >>= guardNotStartsWith(metadataPrefix) >>= stringToId).leftMap(InvalidInput(_))

  //

  private[sec] val guardNonEmptyName: String => Attempt[String] = guardNonEmpty("name")

  private[sec] def normal(name: String): Attempt[Normal] =
    (guardNonEmptyName(name) >>= guardNotStartsWith(systemPrefix)).map(Normal.unsafe)

  private[sec] def system(name: String): Attempt[System] =
    (guardNonEmptyName(name) >>= guardNotStartsWith(systemPrefix)).map(System.unsafe)

  private[sec] val streamIdToString: StreamId => String =
    case id: Id     => idToString(id)
    case MetaId(id) => s"$metadataPrefix${idToString(id)}"

  private[sec] val stringToStreamId: String => Attempt[StreamId] =
    case id if id.startsWith(metadataPrefix) => stringToId(id.substring(metadataPrefixLength)).map(MetaId(_))
    case id                                  => stringToId(id)

  private[sec] val idToString: Id => String =
    case System(n) => s"$systemPrefix$n"
    case Normal(n) => n

  private[sec] val stringToId: String => Attempt[Id] =
    case sid if sid.startsWith(systemPrefix) => system(sid.substring(systemPrefixLength))
    case sid                                 => normal(sid)

  private[sec] val systemPrefix: String      = "$"
  private[sec] val systemPrefixLength: Int   = systemPrefix.length
  private[sec] val metadataPrefix: String    = "$$"
  private[sec] val metadataPrefixLength: Int = metadataPrefix.length

  //

  extension (sid: StreamId)

    def fold[A](nfn: Normal => A, sfn: System => A, mfn: MetaId => A): A = sid match
      case n: Normal => nfn(n)
      case s: System => sfn(s)
      case m: MetaId => mfn(m)

    def stringValue: String     = streamIdToString(sid)
    def render: String          = stringValue
    def isNormal: Boolean       = fold(_ => true, _ => false, _ => false)
    def isSystemOrMeta: Boolean = fold(_ => false, _ => true, _ => true)

  extension (id: Id) def metaId: MetaId = MetaId(id)

  given Eq[StreamId] = Eq.fromUniversalEquals[StreamId]

end StreamId

//======================================================================================================================




© 2015 - 2024 Weber Informatics LLC | Privacy Policy