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

com.comcast.ip4s.MulticastJoin.scala Maven / Gradle / Ivy

There is a newer version: 3.6.0
Show newest version
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * 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 com.comcast.ip4s

import cats.{Order, Show}

/** Represents a join of a multicast group.
  *
  * This is represented as an ADT consisting of two constructors, [[AnySourceMulticastJoin]] and
  * [[SourceSpecificMulticastJoin]]. These constructors are provided as top level types to allow domain modeling where a
  * specific join type is required. The address type is parameterized for a similar reason -- to allow domain modeling
  * where a specific address type is required.
  */
sealed abstract class MulticastJoin[+A <: IpAddress] extends Product with Serializable {

  /** Converts this join to a value of type `A` using the supplied functions. */
  def fold[B](asm: AnySourceMulticastJoin[A] => B, ssm: SourceSpecificMulticastJoin[A] => B): B =
    this match {
      case a: AnySourceMulticastJoin[A]      => asm(a)
      case a: SourceSpecificMulticastJoin[A] => ssm(a)
    }

  /** Narrows to an `AnySourceMulticastJoin`. */
  def asAsm: Option[AnySourceMulticastJoin[A]] = fold(Some(_), _ => None)

  /** Narrows to a `SourceSpecificMulticastJoin`. */
  def asSsm: Option[SourceSpecificMulticastJoin[A]] = fold(_ => None, Some(_))

  /** Returns the source address and group address. If this join is an any-source join, `None` is returned for the
    * source. Otherwise, this join is a source specific join and `Some(src)` is returned for the source.
    */
  def sourceAndGroup: (Option[A], Multicast[A]) =
    fold(asm => (None, asm.group), ssm => (Some(ssm.source), ssm.group))

  override def toString: String =
    fold(asm => asm.group.toString, ssm => s"${ssm.source}@${ssm.group}")
}

object MulticastJoin {

  /** Constructs an `AnySourceMulticastJoin[A]`. */
  def asm[A <: IpAddress](group: Multicast[A]): MulticastJoin[A] =
    AnySourceMulticastJoin(group)

  /** Constructs a `SourceSpecificMulticastJoin[A]`. */
  def ssm[A <: IpAddress](source: A, group: SourceSpecificMulticast[A]): MulticastJoin[A] =
    SourceSpecificMulticastJoin(source, group)

  def fromString(value: String): Option[MulticastJoin[IpAddress]] =
    fromStringGeneric(value, IpAddress.fromString)

  def fromString4(value: String): Option[MulticastJoin[Ipv4Address]] =
    fromStringGeneric(value, Ipv4Address.fromString)

  def fromString6(value: String): Option[MulticastJoin[Ipv6Address]] =
    fromStringGeneric(value, Ipv6Address.fromString)

  private val Pattern = """(?:([^@]+)@)?(.+)""".r
  private[ip4s] def fromStringGeneric[A <: IpAddress](
      value: String,
      parse: String => Option[A]
  ): Option[MulticastJoin[A]] =
    value match {
      case Pattern(sourceStr, groupStr) =>
        Option(sourceStr) match {
          case Some(sourceStr) =>
            for {
              source <- parse(sourceStr)
              group <- parse(groupStr).flatMap(_.asSourceSpecificMulticastLenient)
            } yield ssm(source, group)
          case None =>
            for {
              group <- parse(groupStr).flatMap(_.asSourceSpecificMulticastLenient)
            } yield asm(group)
        }
      case _ => None
    }

  implicit def order[J[x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]: Order[J[A]] =
    Order.fromOrdering(MulticastJoin.ordering[J, A])
  implicit def ordering[J[x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]: Ordering[J[A]] =
    Ordering.by(_.sourceAndGroup)
  implicit def show[J[x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]: Show[J[A]] =
    Show.fromToString[J[A]]
}

/** Multicast join to a group without a source filter. */
final case class AnySourceMulticastJoin[+A <: IpAddress](group: Multicast[A]) extends MulticastJoin[A]

object AnySourceMulticastJoin {
  def fromString(value: String): Option[AnySourceMulticastJoin[IpAddress]] =
    MulticastJoin.fromStringGeneric(value, IpAddress.fromString).flatMap(_.asAsm)

  def fromString4(value: String): Option[AnySourceMulticastJoin[Ipv4Address]] =
    MulticastJoin.fromStringGeneric(value, Ipv4Address.fromString).flatMap(_.asAsm)

  def fromString6(value: String): Option[AnySourceMulticastJoin[Ipv6Address]] =
    MulticastJoin.fromStringGeneric(value, Ipv6Address.fromString).flatMap(_.asAsm)
}

/** Multicast join to a group from the specified source. */
final case class SourceSpecificMulticastJoin[+A <: IpAddress](source: A, group: SourceSpecificMulticast[A])
    extends MulticastJoin[A]

object SourceSpecificMulticastJoin {
  def fromString(value: String): Option[SourceSpecificMulticastJoin[IpAddress]] =
    MulticastJoin.fromStringGeneric(value, IpAddress.fromString).flatMap(_.asSsm)

  def fromString4(value: String): Option[SourceSpecificMulticastJoin[Ipv4Address]] =
    MulticastJoin.fromStringGeneric(value, Ipv4Address.fromString).flatMap(_.asSsm)

  def fromString6(value: String): Option[SourceSpecificMulticastJoin[Ipv6Address]] =
    MulticastJoin.fromStringGeneric(value, Ipv6Address.fromString).flatMap(_.asSsm)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy