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

simpleivr.Sayable.scala Maven / Gradle / Ivy

package simpleivr

import cats.effect.IO
import sourcecode.Name


sealed trait Sayable {
  final def &(that: Sayable) = (this, that) match {
    case (Sayable.Empty, _)                         => that
    case (_, Sayable.Empty)                         => this
    case (Sayable.Many(msgs1), Sayable.Many(msgs2)) => Sayable.Many(msgs1 ++ msgs2)
    case (Sayable.Many(msgs1), msg2)                => Sayable.Many(msgs1 :+ msg2)
    case (msg1, Sayable.Many(msgs2))                => Sayable.Many(msg1 +: msgs2)
    case (msg1, msg2)                               => Sayable.Many(List(msg1, msg2))
  }
  final def toSingles: Seq[Sayable.Single] = this match {
    case single: Sayable.Single => Seq(single)
    case Sayable.Many(sayables) => sayables.flatMap(_.toSingles)
    case Sayable.Group(sayable) => sayable.toSingles
  }
  override def toString =
    toSingles
      .collect {
        case speak: Speaks#Speak          => speak.msg
        case Pause(_)                     => "..."
        case Play(AudioPath(pathAndName)) => s"[$pathAndName]"
      }
      .mkString(" ")
}
object Sayable {
  sealed trait Single extends Sayable
  case class Group(sayable: Sayable) extends Sayable
  case class Many(sayables: Seq[Sayable]) extends Sayable

  val Empty: Sayable = Many(Nil)

  def apply(sayables: Seq[Sayable]) = sayables match {
    case Seq()       => Empty
    case Seq(single) => single
    case many        => Many(many)
  }

  def unapplySeq(sayable: Sayable): Option[Seq[Any]] = Some(sayable.toSingles.map {
    case speak: Speaks#Speak => speak.msg
    case s                   => s
  })

  trait Folder[A] extends (Sayable => A) {
    def apply(sayable: Sayable): A = sayable match {
      case Pause(ms)       => pause(ms)
      case Play(path)      => play(path)
      case s: Speaks#Speak => speak(s)
      case Group(s)        => group(s)
      case Many(sayables)  => many(sayables.toList)
    }

    def pause(ms: Int): A
    def play(path: AudioPath): A
    def speak(spk: Speaks#Speak): A
    def group(sayable: Sayable): A
    def many(sayables: List[Sayable]): A
  }
}

case class Pause(ms: Int) extends Sayable.Single

case class Play(path: AudioPath) extends Sayable.Single
object Play {
  def ifExists(files: AudioFiles, path: AudioPath): IO[Option[Play]] =
    files.exists.map {
      case true  => Some(Play(path))
      case false => None
    }
}

trait Speaks {
  def audioFileBackend: AudioFileBackend

  protected def Speak(msg: String) = new Speak(msg)
  object Speak {
    def unapply(s: Speak) = Some(s.msg)
  }
  class Speak private[Speaks](val msg: String) extends Sayable.Single {
    def backend: AudioFileBackend = Speaks.this.audioFileBackend
    def files = backend.speakFiles(this)
    def path = backend.speakPath(this)
    override def toString = s"Speak($msg)"
  }

  protected def speak(implicit name: Name) = Speak(name.value)

  def speaks: List[Speak] =
    getClass.getMethods.toList
      .filter(classOf[Speak] isAssignableFrom _.getReturnType)
      .filter(_.getParameterCount == 0)
      .map(_.invoke(this).asInstanceOf[Speak])
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy