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

smithy4s.internals.SchemaDescriptionDetailed.scala Maven / Gradle / Ivy

There is a newer version: 0.19.0-41-91762fb
Show newest version
/*
 *  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
package internals

import smithy4s.schema._
private[internals] trait SchemaDescriptionDetailedImpl[A]
    extends (Set[ShapeId] => (Set[ShapeId], String)) {
  def mapResult[B](f: String => String): SchemaDescriptionDetailedImpl[B] = {
    seen =>
      val (s1, desc) = apply(seen)
      (s1 ++ seen, f(desc))
  }
  def flatMapResult[B](
      f: String => SchemaDescriptionDetailedImpl[B]
  ): SchemaDescriptionDetailedImpl[B] = { seen =>
    val (s1, desc) = apply(seen)
    f(desc)(s1 ++ seen)
  }
}

private[internals] object SchemaDescriptionDetailedImpl
    extends SchemaVisitor[SchemaDescriptionDetailedImpl] { self =>

  def of[A](shapeId: ShapeId, value: String): SchemaDescriptionDetailedImpl[A] =
    s => (s + shapeId, value)

  override def primitive[P](
      shapeId: ShapeId,
      hints: Hints,
      tag: Primitive[P]
  ): SchemaDescriptionDetailedImpl[P] = {
    SchemaDescriptionDetailedImpl.of(
      shapeId,
      SchemaDescription.apply(tag.schema(shapeId))
    )
  }
  override def collection[C[_], A](
      shapeId: ShapeId,
      hints: Hints,
      tag: CollectionTag[C],
      member: Schema[A]
  ): SchemaDescriptionDetailedImpl[C[A]] = {
    apply(member).mapResult(s => s"${tag.name}[$s]")
  }
  override def map[K, V](
      shapeId: ShapeId,
      hints: Hints,
      key: Schema[K],
      value: Schema[V]
  ): SchemaDescriptionDetailedImpl[Map[K, V]] = {
    apply(key).flatMapResult { kDesc =>
      apply(value).mapResult { vDesc =>
        s"Map[$kDesc, $vDesc]"
      }
    }
  }
  override def enumeration[E](
      shapeId: ShapeId,
      hints: Hints,
      tag: EnumTag[E],
      values: List[EnumValue[E]],
      total: E => EnumValue[E]
  ): SchemaDescriptionDetailedImpl[E] = {
    val vDesc = values.map(e => s""""${e.stringValue}"""").mkString(", ")
    SchemaDescriptionDetailedImpl.of(shapeId, s"enum($vDesc)")
  }

  override def struct[S](
      shapeId: ShapeId,
      hints: Hints,
      fields: Vector[Field[S, _]],
      make: IndexedSeq[Any] => S
  ): SchemaDescriptionDetailedImpl[S] = { seen =>
    def forField[T](sf: Field[S, T]): (String, (Set[ShapeId], String)) = {
      apply(sf.schema)(seen)
      sf.label -> apply(sf.schema)(seen)
    }
    val (sFinal, res) = fields
      .foldLeft((Set.empty[ShapeId], Seq.empty[(String, String)])) {
        case ((shapes, fieldDesc), field) =>
          val (label, (s2, desc)) = forField(field)
          (shapes ++ s2, fieldDesc :+ (label -> desc))
      }
    val fieldDesc =
      res.map { case (label, desc) => s"$label: $desc" }.mkString(", ")
    sFinal -> s"structure ${shapeId.name}($fieldDesc)"
  }

  override def union[U](
      shapeId: ShapeId,
      hints: Hints,
      alternatives: Vector[Alt[U, _]],
      dispatch: Alt.Dispatcher[U]
  ): SchemaDescriptionDetailedImpl[U] = { seen =>
    def forAlt[T](alt: Alt[U, T]): (String, (Set[ShapeId], String)) = {
      val desc = apply(alt.schema)(seen)
      alt.label -> desc
    }
    val (sFinal, res) = alternatives
      .foldLeft((Set.empty[ShapeId], Seq.empty[(String, String)])) {
        case ((shapes, fieldDesc), alt) =>
          val (label, (s2, desc)) = forAlt(alt)
          (shapes ++ s2, fieldDesc :+ (label -> desc))
      }
    val fieldDesc =
      res.map { case (label, desc) => s"$label: $desc" }.mkString(" | ")
    sFinal -> s"union ${shapeId.name}($fieldDesc)"
  }

  override def biject[A, B](
      schema: Schema[A],
      bijection: Bijection[A, B]
  ): SchemaDescriptionDetailedImpl[B] = {
    apply(schema).mapResult(identity)
  }

  override def refine[A, B](
      schema: Schema[A],
      refinement: Refinement[A, B]
  ): SchemaDescriptionDetailedImpl[B] = {
    apply(schema).mapResult { desc => s"Refinement[$desc]" }
  }
  override def lazily[A](
      suspend: Lazy[Schema[A]]
  ): SchemaDescriptionDetailedImpl[A] = {
    new SchemaDescriptionDetailedImpl[A] {
      val rec = suspend.map(s => s -> self.apply(s))
      override def apply(seen: Set[ShapeId]): (Set[ShapeId], String) = {
        val (schema, f) = rec.value
        if (seen(schema.shapeId)) {
          seen -> s"Recursive[${schema.shapeId.name}]"
        } else {
          f(seen + schema.shapeId)
        }
      }
    }
  }

  override def option[A](
      schema: Schema[A]
  ): SchemaDescriptionDetailedImpl[Option[A]] =
    apply(schema).mapResult { desc => s"Option[$desc]" }

  val conversion: SchemaDescriptionDetailedImpl ~> SchemaDescription =
    new (SchemaDescriptionDetailedImpl ~> SchemaDescription) {
      def apply[A](
          fa: SchemaDescriptionDetailedImpl[A]
      ): SchemaDescription[A] = fa(Set.empty)._2
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy