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

advxml.transform.AbstractRule.scala Maven / Gradle / Ivy

package advxml.transform

import advxml.utils.XmlUtils
import cats.{MonadThrow, Monoid, Semigroup}
import cats.syntax.all.*

import scala.xml.{Elem, NodeSeq}

trait AbstractRule
object AbstractRule extends AbstractRuleInstances {
  case class And(a: AbstractRule, b: AbstractRule) extends AbstractRule
  case class OrElse(a: AbstractRule, b: AbstractRule) extends AbstractRule
  case class Optional(a: AbstractRule) extends AbstractRule

  def transform[F[_]](root: NodeSeq, rule: AbstractRule, rules: AbstractRule*)(implicit
    F: MonadThrow[F]
  ): F[NodeSeq] =
    transform(root, List(rule) ++ rules)

  def transform[F[_]](root: NodeSeq, rules: List[AbstractRule])(implicit
    F: MonadThrow[F]
  ): F[NodeSeq] =
    rules.foldLeft(F.pure(root))((actDoc, rule) => actDoc.flatMap(doc => transform(doc, rule)))

  def transform[F[_]](doc: NodeSeq, rule: AbstractRule)(implicit F: MonadThrow[F]): F[NodeSeq] =
    rule match {
      case OrElse(a, b)  => transform[F](doc, a).handleErrorWith(_ => transform(doc, b))
      case And(a, b)     => transform[F](doc, a).flatMap(transform(_, b))
      case Optional(a)   => transform[F](doc, a).handleErrorWith(_ => F.pure(doc))
      case rule: XmlRule => XmlRule.transform(doc, rule)
    }
}
sealed trait AbstractRuleInstances {
  implicit val composableAbstractRuleWithAndOpSemigroupInstance: Semigroup[AbstractRule] =
    (x: AbstractRule, y: AbstractRule) => AbstractRule.And(x, y)
}

//-------------------- SINGLE XML RULE --------------------
sealed trait XmlRule extends AbstractRule {
  val zoom: XmlZoom
}
object XmlRule {

  import cats.syntax.all.*
  import ComposableXmlModifier.*

  private[transform] def transform[F[_]](root: NodeSeq, rule: XmlRule)(implicit
    F: MonadThrow[F]
  ): F[NodeSeq] = {
    val modifier = rule match {
      case r: ComposableXmlRule => Monoid.combineAll(r.modifiers)
      case r: FinalXmlRule      => r.modifier
    }
    buildRewriteRule(root, rule.zoom, modifier)
  }

  private def buildRewriteRule[F[_]](root: NodeSeq, zoom: XmlZoom, modifier: XmlModifier)(implicit
    F: MonadThrow[F]
  ): F[NodeSeq] = {
    for {
      target <- zoom.detailed[F](root)
      targetNodeSeq = target.nodeSeq
      targetParents = target.parents
      updatedTarget <- modifier[F](targetNodeSeq)
      updatedWholeDocument = {
        targetParents
          .foldRight(XmlPatch.const(targetNodeSeq, updatedTarget))((parent, patch) =>
            XmlPatch(
              parent,
              _.flatMap { case e: Elem =>
                XmlUtils.flatMapChildren(
                  e,
                  n =>
                    patch.zipWithUpdated
                      .getOrElse(Some(n), Some(n))
                      .getOrElse(NodeSeq.Empty)
                )
              }
            )
          )
          .updated
      }
    } yield updatedWholeDocument
  }

  // ============================== BUILD ==============================
  def apply(
    zoom: XmlZoom,
    modifier1: ComposableXmlModifier,
    modifiers: ComposableXmlModifier*
  ): ComposableXmlRule =
    Impls.Composable(zoom, (modifier1 +: modifiers).toList)

  def apply(zoom: XmlZoom, modifiers: List[ComposableXmlModifier]): ComposableXmlRule =
    Impls.Composable(zoom, modifiers)

  def apply(zoom: XmlZoom, modifier: FinalXmlModifier): FinalXmlRule =
    Impls.Final(zoom, modifier)

  // ============================== IMPLS ==============================
  private object Impls {

    case class Composable(zoom: XmlZoom, modifiers: List[ComposableXmlModifier])
        extends ComposableXmlRule {

      override def withModifier(modifier: ComposableXmlModifier): ComposableXmlRule =
        copy(modifiers = modifiers :+ modifier)
    }

    case class Final(zoom: XmlZoom, modifier: FinalXmlModifier) extends FinalXmlRule

  }
}

sealed trait ComposableXmlRule extends XmlRule {
  val modifiers: List[ComposableXmlModifier]
  def withModifier(modifier: ComposableXmlModifier): ComposableXmlRule
}
sealed trait FinalXmlRule extends XmlRule {
  val modifier: FinalXmlModifier
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy