Skip to content

Commit

Permalink
Added features used in other projects
Browse files Browse the repository at this point in the history
  • Loading branch information
hohonuuli committed Aug 8, 2024
1 parent b30443a commit a3c77fb
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 8 deletions.
16 changes: 8 additions & 8 deletions scommons/src/main/scala/org/mbari/scommons/etc/jdk/Futures.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package org.mbari.scommons.etc.jdk

import java.time.Duration
import java.time.Duration as JDuration
import java.util.concurrent.{Executors, TimeUnit}
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.concurrent.duration.Duration as SDuration
import scala.util.Try
import scala.jdk.DurationConverters.*

object Futures:
val DefaultTimeout = Duration.ofSeconds(10)
val DefaultTimeout = JDuration.ofSeconds(10)

/**
* Run a Future and return the result or an Exception if the Future fails or does not complete within the timeout
Expand All @@ -21,13 +22,12 @@ object Futures:
* @return
* The result of the Future or an Exception
*/
def safeRunSync[T](f: => Future[T], timeout: Duration)(using ec: ExecutionContext): Either[Throwable, T] =
def safeRunSync[T](f: => Future[T], timeout: JDuration)(using ec: ExecutionContext): Either[Throwable, T] =
Try(Await.result(f, SDuration(timeout.toMillis, TimeUnit.MILLISECONDS))).toEither

def join[T](f: => Future[T], timeout: Duration)(using ec: ExecutionContext): T =
Await.result(f, SDuration(timeout.toMillis, TimeUnit.MILLISECONDS))

extension [T](f: Future[T])
def join(timeout: Duration = DefaultTimeout)(using ec: ExecutionContext): T = Futures.join(f, timeout)
def safeRunSync(timeout: Duration = DefaultTimeout)(using ec: ExecutionContext): Either[Throwable, T] =
def join: T = join(DefaultTimeout)
def join(duration: JDuration): T = join(duration.toScala)
def join(duration: SDuration): T = Await.result(f, duration)
def safeRunSync(timeout: JDuration = DefaultTimeout)(using ec: ExecutionContext): Either[Throwable, T] =
Futures.safeRunSync(f, timeout)
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ object Uris:
.withCause(e)
.log(s"Failed to connect to $uri")
false

38 changes: 38 additions & 0 deletions scommons/src/main/scala/org/mbari/scommons/etc/sdk/Eithers.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) Monterey Bay Aquarium Research Institute 2021
*
* FathomNet code is non-public software. Unauthorized copying of this file,
* via any medium is strictly prohibited. Proprietary and confidential.
* Written by: Brian Schlining <[email protected]>
*/

package org.mbari.scommons.etc.sdk

import java.util.Optional

object Eithers:

private val emptyOptionalError = new NoSuchElementException("Optional is empty")

/**
* Helper to traverse a sequence of items that can fail
*
* @param seq
* The sequence of items
* @param f
* The function to apply to each item in the sequence
* @return
* A sequence of items that have been transformed by the function
*/
def traverse[A, B](seq: Seq[A])(f: A => Either[Throwable, B]): Either[Throwable, Seq[B]] =
seq.foldLeft(Right(Seq.empty): Either[Throwable, Seq[B]]) { (acc, a) =>
for
xs <- acc
x <- f(a)
yield xs :+ x
}

extension [B](opt: Optional[B])
def toEither: Either[Throwable, B] =
if opt.isPresent then Right(opt.get)
else Left(emptyOptionalError)
35 changes: 35 additions & 0 deletions scommons/src/main/scala/org/mbari/scommons/etc/sdk/IO.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) Monterey Bay Aquarium Research Institute 2021
*
* FathomNet code is non-public software. Unauthorized copying of this file,
* via any medium is strictly prohibited. Proprietary and confidential.
* Written by: Brian Schlining <[email protected]>
*/

package org.mbari.scommons.etc.sdk

import scala.concurrent.Future
import scala.concurrent.ExecutionContext

type IO[A, B] = A => Either[Throwable, B]
type AsyncIO[A, B] = A => Future[B]

object IO:

extension [A, B](io: IO[A, B])

def unit: IO[A, Unit] = a => Right(())

def flatMap[C](f: IO[B, C]): IO[A, C] = a => io(a).flatMap(b => f(b))

def map[C](f: B => C): IO[A, C] = a => io(a).map(f)

def foreach(f: B => Unit): IO[A, Unit] = a =>
for b <- io(a)
yield f(b)

def async(using executionContext: ExecutionContext): AsyncIO[A, B] = a =>
Future(io(a)).map {
case Right(b) => b
case Left(e) => throw e
}
13 changes: 13 additions & 0 deletions scommons/src/main/scala/org/mbari/scommons/etc/sdk/Iterables.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.mbari.scommons.etc.sdk

import scala.concurrent.Future
import org.mbari.scommons.etc.jdk.Futures.*
import scala.concurrent.duration.Duration
import scala.concurrent.ExecutionContext

object Iterables:

extension [A, B <: Iterable[A]](xs: B)
def parMap[C](f: A => C)(using executionContext: ExecutionContext): Iterable[C] =
xs.map(x => Future { f(x) })
.map(_.join(Duration.Inf))

0 comments on commit a3c77fb

Please sign in to comment.