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

playground.smithyql.NodeContext.scala Maven / Gradle / Ivy

The newest version!
package playground.smithyql

import cats.data.Chain
import cats.data.Chain.==:
import cats.syntax.all.*

// The path to a position in the parsed source
sealed trait NodeContext extends Product with Serializable with NodeContext.PathEntry.TraversalOps {

  def render: String =
    this match {
      case NodeContext.Impl(context) =>
        import NodeContext.PathEntry.*

        context
          .map {
            case AtPrelude          => ".prelude"
            case InQuery(index)     => s".query($index)"
            case AtUseClause(index) => s".useClause($index)"
            case AtOperationName    => ".operationName"
            case AtOperationInput   => ".input"
            case CollectionEntry(i) => s".[${i.getOrElse("")}]"
            case StructValue(key)   => s".$key"
            case StructBody         => ".{}"
            case Quotes             => ".\"\""
          }
          .mkString_("")

    }

  def append(
    elem: NodeContext.PathEntry
  ): NodeContext =
    this match {
      case NodeContext.Impl(context) => NodeContext.Impl(context.append(elem))
    }

  def length: Long =
    this match {
      case NodeContext.Impl(context) => context.size
    }

  def toList: List[NodeContext.PathEntry] =
    this match {
      case NodeContext.Impl(ctx) => ctx.toList
    }

  def uncons: Option[
    (
      NodeContext.PathEntry,
      NodeContext,
    )
  ] =
    this match {
      case NodeContext.Impl(h ==: t) => Some((h, NodeContext.Impl(t)))
      case _                         => None
    }

  def ^^:(
    item: NodeContext.PathEntry
  ): NodeContext =
    this match {
      case NodeContext.Impl(context) => NodeContext.Impl(context.prepend(item))
    }

}

object NodeContext {

  object ^^: {

    /** Extractor for path entry prefixes. You can use this to assert on a prefix of a path (when
      * you consider paths L->R). For example, in a file like "hello {}", the path at "hello" would
      * be something like
      *
      * {{{EmptyPath.inQuery(0).inOperationName}}}
      *
      * which could be matched with the following pattern:
      *
      * {{{case InQuery(0) ^^: InOperationName ^^: EmptyPath}}}
      *
      * or, if you only want to match on a prefix and keep the rest up for more matching:
      *
      * {{{case InQuery(0) ^^: rest}}}
      */
    def unapply(
      items: NodeContext
    ): Option[
      (
        PathEntry,
        NodeContext,
      )
    ] = items.uncons

  }

  val EmptyPath: NodeContext = Impl(Chain.nil)

  private final case class Impl(
    context: Chain[PathEntry]
  ) extends NodeContext

  sealed trait PathEntry extends Product with Serializable

  object PathEntry {
    case object AtPrelude extends PathEntry

    case class InQuery(
      index: Int
    ) extends PathEntry

    final case class AtUseClause(
      index: Int
    ) extends PathEntry

    case object AtOperationName extends PathEntry
    case object AtOperationInput extends PathEntry

    final case class StructValue(
      key: String
    ) extends PathEntry

    // no index if it's not in an entry - todo replace with CollectionBody?
    final case class CollectionEntry(
      index: Option[Int]
    ) extends PathEntry

    case object StructBody extends PathEntry
    case object Quotes extends PathEntry

    trait TraversalOps {
      self: NodeContext =>

      def inQuery(
        index: Int
      ): NodeContext = append(PathEntry.InQuery(index))

      def inPrelude: NodeContext = append(PathEntry.AtPrelude)

      def inUseClause(
        index: Int
      ): NodeContext = append(PathEntry.AtUseClause(index))

      def inOperationName: NodeContext = append(PathEntry.AtOperationName)
      def inOperationInput: NodeContext = append(PathEntry.AtOperationInput)

      def inStructValue(
        key: String
      ): NodeContext = append(PathEntry.StructValue(key))

      def inCollectionEntry(
        index: Option[Int]
      ): NodeContext = append(
        PathEntry.CollectionEntry(index)
      )

      def inStructBody: NodeContext = append(PathEntry.StructBody)
      def inQuotes: NodeContext = append(PathEntry.Quotes)

    }

  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy