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

smithy4s.codegen.internals.ToLines.scala Maven / Gradle / Ivy

/*
 *  Copyright 2021-2024 Disney Streaming
 *
 *  Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     https://disneystreaming.github.io/TOST-1.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package smithy4s.codegen.internals

import cats.kernel.Monoid
import cats.syntax.all._

import LineSegment.{Literal, NameRef}
import cats.Foldable

/**
  * Construct allowing to flatten arbitrary levels of nested lists
  */
private[internals] trait ToLines[A] {
  def render(a: A): Lines
}

private[internals] object ToLines {

  def render[A](a: A)(implicit A: ToLines[A]): Lines = A.render(a)
  implicit val identity: ToLines[Lines] = r => r

  implicit def foldableRenderable[L[_]: Foldable, A](implicit
      A: ToLines[A]
  ): ToLines[L[A]] = { (l: L[A]) =>
    val lines: List[List[Line]] = l.toList.map(A.render).map(r => r.list)
    Lines(lines.flatten)
  }

  implicit def tupleRenderable[A](implicit
      A: ToLines[A]
  ): ToLines[(String, A)] = (t: (String, A)) => A.render(t._2).addImport(t._1)

  implicit def lineToLines[A: ToLine]: ToLines[A] = (a: A) => {
    val line = ToLine[A].render(a)
    // empty string must be treated like an empty list which is a Monoid Empty as oposed to wrapping in a singleton list which will render a new line character`
    if (line.segments.isEmpty) Lines.empty else Lines(line)
  }

}

// Models

private[internals] case class Lines(list: List[Line]) {
  def isEmpty: Boolean = list.isEmpty

  def block(l: LinesWithValue*): Lines = {
    val openBlock: List[Line] =
      list.lastOption.flatMap(_.segments.lastOption).collect {
        case hardcoded: Literal =>
          hardcoded.value match {
            case ")"   => Line("){")
            case "}"   => Line("}{")
            case other => Line(other + " {")
          }
      } match {
        case Some(value) => list.dropRight(1) :+ value
        case None        => list
      }

    Lines(openBlock) ++ indent(
      l.toList.foldMap(_.render)
    ) ++ Lines("}")
  }

  def appendToLast(s: String): Lines = {
    val newLines = list.lastOption.map(_ + Line(s)) match {
      case Some(value) => list.dropRight(1) :+ value
      case None        => list
    }
    Lines(newLines)
  }

  def transformLines(f: List[Line] => List[Line]): Lines = Lines(f(list))
  def mapLines(f: Line => Line): Lines = transformLines(_.map(f))

  def ++(other: Lines): Lines =
    Lines(list ++ other.list)

  def addImport(im: String): Lines =
    if (im.nonEmpty) Lines(list :+ NameRef(im).toLine) else this
  def addImports(im: Set[String]): Lines =
    Lines(list ::: im.map(s => NameRef(s).toLine).toList)

  def when(cond: => Boolean): Lines =
    if (cond) this else Lines.empty
}
private[internals] object Lines {
  def apply(line: Line): Lines = Lines(List(line))
  def apply(str: String): Lines = Lines(List(Line(str)))
  val empty = Lines(List.empty[Line])
  implicit val linesMonoid: Monoid[Lines] = Monoid.instance(empty, _ ++ _)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy