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

sttp.model.headers.Range.scala Maven / Gradle / Ivy

The newest version!
package sttp.model.headers

import sttp.model.ContentRangeUnits
import sttp.model.internal.ParseUtils
import sttp.model.internal.Validate.RichEither

case class Range(start: Option[Long], end: Option[Long], unit: String) {

  override def toString: String = {
    val sb = new java.lang.StringBuilder()
    sb.append(unit).append("=")
    start match {
      case x: Some[Long] => sb.append(x.value)
      case _             => ()
    }
    sb.append('-')
    end match {
      case x: Some[Long] => sb.append(x.value)
      case _             => ()
    }
    sb.toString
  }

  def toContentRange(fileSize: Long, unit: String = ContentRangeUnits.Bytes): ContentRange =
    ContentRange(
      unit, {
        if (start.isDefined && end.isDefined) Some((start.get, end.get))
        else None
      },
      Some(fileSize)
    )

  val contentLength: Long =
    if (end.isDefined && start.isDefined) end.get - start.get + 1
    else 0

  def isValid(contentSize: Long): Boolean =
    (start, end) match {
      case (Some(_start), Some(_end)) => _start <= _end && _end < contentSize
      case (Some(_start), None)       => _start < contentSize
      case (None, Some(_end))         => _end < contentSize
      case _                          => false
    }
}

object Range {

  def parse(str: String): Either[String, List[Range]] =
    str.split("=") match {
      case Array(unit, s) =>
        val ranges = processString(s, unit)
        if (ranges.forall(validateRange) && ranges.nonEmpty) Right(ranges.reverse)
        else Left("Invalid Range")
      case _ => Left("Expected range in the format: \"unit=start/end\", but got: %s".format(str))
    }

  private def processString(s: String, unit: String): List[Range] =
    s.split(",").map(parseSingleRange(_, unit)).reverse.toList // TODO: do we need `.reverse` here yet?

  private def parseSingleRange(rangeString: String, unit: String): Range =
    rangeString.trim.split("-") match {
      case Array(start, end) => Range(ParseUtils.toLongOption(start), ParseUtils.toLongOption(end), unit)
      case Array(start)      => Range(ParseUtils.toLongOption(start), None, unit)
      case _                 => Range(None, None, unit)
    }

  private def validateRange(range: Range): Boolean =
    (range.start, range.end) match {
      case (Some(start), Some(end)) => start <= end
      case (None, None)             => false
      case _                        => true
    }

  def unsafeParse(s: String): List[Range] = parse(s).getOrThrow

  def unsafeApply(start: Option[Long], end: Option[Long], unit: String): List[Range] =
    safeApply(start, end, unit).getOrThrow

  def safeApply(start: Option[Long], end: Option[Long], unit: String): Either[String, List[Range]] = {
    val range = Range(start, end, unit)
    if (validateRange(range)) Right(List(range))
    else Left("Invalid Range")
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy