<commit_before>package busymachines.core.exceptions

/**
  * ``THERE'S NOTHING WRONG WITH FAILURE!``
  *
  * There, now that that got your attention, let me elaborate on the
  * intention of these types.
  *
  * Basically, they are a semantically richer way of expressing common
  * failures when developing backend-servers.
  *
  * Furthermore, these exceptions being part of the ``core`` of the application
  * —by reading this file— you have not gauged their full potentiality, yet.
  * The intention is to give richer interpretations to these "common failures"
  * in other ``busymachines-commons`` module than could be done to the likes
  * of [[Throwable]].
  *
  * The reason why there is a trait [[FailureMessage]], and some types
  * that extend [[Exception]] is that the potentiality of these types can
  * be achieved either through a monad stack approach to building applications,
  * or to a more vanilla scala approach, respectively.
  *
  * There are two quasi-parallel hierarchies of failures:
  * I) the [[FailureMessage]] representing one single failure
  * II) the [[FailureMessages]] representing a container of multiple [[FailureMessage]].
  * The intended use of the [[FailureMessages.id]] (and other ones inherited from [[FailureMessage]]
  * is to signal the general "context" within which the specific [[FailureMessages.messages]]
  * where gathered.
  *
  * There are the following semantically meaningful exceptions (with their plural counterparts elided)
  * that you ought to be using:
  * - [[NotFoundFailure]]     -  [[SemanticFailures.NotFound]]
  * - [[ForbiddenFailure]]    -  [[SemanticFailures.Forbidden]]
  * - [[DeniedFailure]]       -  [[SemanticFailures.Denied]]
  * - [[InvalidInputFailure]] -  [[SemanticFailures.InvalidInput]]
  * - [[ConflictFailure]]     -  [[SemanticFailures.Conflict]]
  *
  * Each described in the [[SemanticFailures]] docs.
  *
  * These definitions are also quite flexible enough to allow multiple styles of active
  * development:
  *
  * 1) The quick and dirty, "better than RuntimeException" style. Basically,
  * you just wind up using the default constructors on the companion object,
  * or the companion object itself
  * {{{
  *   //I know I am in the middle of my domain problem, and I know that here
  *   //I can fail because "I did not find something", so I just quickly use:
  *
  *   option.getOrElse(throw NotFoundFailure)
  *   option.getOrElse(throw NotFoundFailure("this specific option, instead of generic"))
  *
  * }}}
  *
  * This style should be kept just that, "quick and dirty". After the first iteration
  * of the implementation these failures should be replaced by the ones in style 2)
  *
  * 2) The long term style. Where you subclass [[NotFoundFailure]] into a more meaningful
  * exception specific to your problem domain, supply some context via the parameters,
  * and assign it an (preferably) application-wide unique [[FailureID]].
  *
  * {{{
  *   object RevolutionaryDomainFailures {
  *     case object CannotBeDone extends FailureID { val name = "rd_001" }
  *     //... and many others
  *   }
  *
  *   case class SolutionNotFoundFailure(problem: String) extends NotFoundFailure(
  *     s"Solution to problem $problem not found."
  *   ) {
  *     override def id: FailureID = RevolutionaryDomainFailures.CannotBeDone
  *
  *     override def parameters: Parameters = Map(
  *       "problem" -> problem
  *     )
  *   }
  *
  *   object Main {
  *     //...
  *     solutionToPVSNP.getOrElse(throw SolutionNotFoundFailure("P vs. NP"))
  *     solutionToHaltingProblem.getOrElse(throw SolutionNotFoundFailure("Halting Problem"))
  *     //how refined you want your failures, that's up to you.
  *   }
  * }}}
  *
  * @author <NAME>, <EMAIL>, <EMAIL>
  * @since 31 Jul 2017
  *
  */
trait FailureID {
  def name: String
}

object FailureID {

  private case class GenericFailureID(override val name: String) extends FailureID

  def apply(id: String): FailureID = GenericFailureID(id)
}

object FailureMessage {

  /**
    * This is a hack until dotty (scala 3.0) comes along with union types.
    * Until then, boiler plate freedom is given by the implicit
    * conversions found in the package object
    */
  sealed trait StringOrSeqString

  case class StringWrapper private[exceptions](s: String) extends StringOrSeqString

  case class SeqStringWrapper private[exceptions](ses: Seq[String]) extends StringOrSeqString

  type Parameters = Map[String, StringOrSeqString]

  private case class GenericFailureMessage(
    override val id: FailureID,
    override val message: String,
    override val parameters: Parameters
  ) extends FailureMessage

  def apply(id: FailureID, message: String, parameters: Parameters = Map.empty): FailureMessage = {
    GenericFailureMessage(id, message, parameters)
  }
}

trait FailureMessage {
  def id: FailureID

  def message: String

  def parameters: FailureMessage.Parameters = Map.empty
}

/**<commit_msg>Add smart constructors to FailureMessage.Paramaters<commit_after>package busymachines.core.exceptions

/**
  * ``THERE'S NOTHING WRONG WITH FAILURE!``
  *
  * There, now that that got your attention, let me elaborate on the
  * intention of these types.
  *
  * Basically, they are a semantically richer way of expressing common
  * failures when developing backend-servers.
  *
  * Furthermore, these exceptions being part of the ``core`` of the application
  * —by reading this file— you have not gauged their full potentiality, yet.
  * The intention is to give richer interpretations to these "common failures"
  * in other ``busymachines-commons`` module than could be done to the likes
  * of [[Throwable]].
  *
  * The reason why there is a trait [[FailureMessage]], and some types
  * that extend [[Exception]] is that the potentiality of these types can
  * be achieved either through a monad stack approach to building applications,
  * or to a more vanilla scala approach, respectively.
  *
  * There are two quasi-parallel hierarchies of failures:
  * I) the [[FailureMessage]] representing one single failure
  * II) the [[FailureMessages]] representing a container of multiple [[FailureMessage]].
  * The intended use of the [[FailureMessages.id]] (and other ones inherited from [[FailureMessage]]
  * is to signal the general "context" within which the specific [[FailureMessages.messages]]
  * where gathered.
  *
  * There are the following semantically meaningful exceptions (with their plural counterparts elided)
  * that you ought to be using:
  * - [[NotFoundFailure]]     -  [[SemanticFailures.NotFound]]
  * - [[ForbiddenFailure]]    -  [[SemanticFailures.Forbidden]]
  * - [[DeniedFailure]]       -  [[SemanticFailures.Denied]]
  * - [[InvalidInputFailure]] -  [[SemanticFailures.InvalidInput]]
  * - [[ConflictFailure]]     -  [[SemanticFailures.Conflict]]
  *
  * Each described in the [[SemanticFailures]] docs.
  *
  * These definitions are also quite flexible enough to allow multiple styles of active
  * development:
  *
  * 1) The quick and dirty, "better than RuntimeException" style. Basically,
  * you just wind up using the default constructors on the companion object,
  * or the companion object itself
  * {{{
  *   //I know I am in the middle of my domain problem, and I know that here
  *   //I can fail because "I did not find something", so I just quickly use:
  *
  *   option.getOrElse(throw NotFoundFailure)
  *   option.getOrElse(throw NotFoundFailure("this specific option, instead of generic"))
  *
  * }}}
  *
  * This style should be kept just that, "quick and dirty". After the first iteration
  * of the implementation these failures should be replaced by the ones in style 2)
  *
  * 2) The long term style. Where you subclass [[NotFoundFailure]] into a more meaningful
  * exception specific to your problem domain, supply some context via the parameters,
  * and assign it an (preferably) application-wide unique [[FailureID]].
  *
  * {{{
  *   object RevolutionaryDomainFailures {
  *     case object CannotBeDone extends FailureID { val name = "rd_001" }
  *     //... and many others
  *   }
  *
  *   case class SolutionNotFoundFailure(problem: String) extends NotFoundFailure(
  *     s"Solution to problem $problem not found."
  *   ) {
  *     override def id: FailureID = RevolutionaryDomainFailures.CannotBeDone
  *
  *     override def parameters: Parameters = Map(
  *       "problem" -> problem
  *     )
  *   }
  *
  *   object Main {
  *     //...
  *     solutionToPVSNP.getOrElse(throw SolutionNotFoundFailure("P vs. NP"))
  *     solutionToHaltingProblem.getOrElse(throw SolutionNotFoundFailure("Halting Problem"))
  *     //how refined you want your failures, that's up to you.
  *   }
  * }}}
  *
  * @author <NAME>, <EMAIL>, <EMAIL>
  * @since 31 Jul 2017
  *
  */
trait FailureID {
  def name: String
}

object FailureID {

  private case class GenericFailureID(override val name: String) extends FailureID

  def apply(id: String): FailureID = GenericFailureID(id)
}

object FailureMessage {

  /**
    * This is a hack until dotty (scala 3.0) comes along with union types.
    * Until then, boiler plate freedom is given by the implicit
    * conversions found in the package object
    */
  sealed trait StringOrSeqString

  case class StringWrapper private[exceptions](s: String) extends StringOrSeqString

  case class SeqStringWrapper private[exceptions](ses: Seq[String]) extends StringOrSeqString

  type Parameters = Map[String, StringOrSeqString]

  object Parameters {
    def apply(ps: (String, StringOrSeqString)*): Parameters = Map.apply(ps: _*)

    def empty: Parameters = Map.empty[String, StringOrSeqString]
  }

  private case class GenericFailureMessage(
    override val id: FailureID,
    override val message: String,
    override val parameters: Parameters
  ) extends FailureMessage

  def apply(id: FailureID, message: String, parameters: Parameters = FailureMessage.Parameters.empty): FailureMessage = {
    GenericFailureMessage(id, message, parameters)
  }
}

trait FailureMessage {
  def id: FailureID

  def message: String

  def parameters: FailureMessage.Parameters = FailureMessage.Parameters.empty
}

/**
{'fraction_non_alphanumeric': 0.0910162002945508, 'fraction_numerical': 0.0027491408934707906, 'mean_word_length': 3.6511415525114157, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3314696', 'n_tokens_mistral': 2911, 'n_tokens_neox': 2765, 'n_words': 1071}