All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
smithy4s.codecs.StringAndBlobCodecs.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
package codecs
import schema._
import smithy4s.schema.Primitive._
import smithy4s.capability.instances.either._
object StringAndBlobCodecs {
object decoders extends CachedSchemaCompiler.Optional.Impl[BlobDecoder] {
def fromSchema[A](
schema: Schema[A],
cache: Cache
): Option[BlobDecoder[A]] =
StringAndBlobReaderVisitor(schema)
}
object encoders extends CachedSchemaCompiler.Optional.Impl[BlobEncoder] {
def fromSchema[A](
schema: Schema[A],
cache: Cache
): Option[BlobEncoder[A]] =
StringAndBlobWriterVisitor(schema)
}
private type MaybeBlobDecoder[A] = Option[BlobDecoder[A]]
private type MaybeBlobEncoder[A] = Option[BlobEncoder[A]]
private object StringAndBlobReaderVisitor
extends SchemaVisitor.Default[MaybeBlobDecoder] {
self =>
override def default[A]: MaybeBlobDecoder[A] = None
override def primitive[P](
shapeId: ShapeId,
hints: Hints,
tag: Primitive[P]
): MaybeBlobDecoder[P] = tag match {
case PString => Some(stringDecoder)
case PBlob => Some(blobDecoder)
case _ => None
}
override def enumeration[E](
shapeId: ShapeId,
hints: Hints,
tag: EnumTag[E],
values: List[EnumValue[E]],
total: E => EnumValue[E]
): MaybeBlobDecoder[E] = {
tag match {
case EnumTag.ClosedStringEnum =>
Some(new BlobDecoder[E] {
def decode(blob: Blob): Either[PayloadError, E] = {
val str = blob.toUTF8String
values.find(_.stringValue == str) match {
case Some(enumValue) => Right(enumValue.value)
case None =>
Left(
PayloadError(
PayloadPath.root,
s"expected one of ${values.mkString(",")}",
s"Unknown enum value $str"
)
)
}
}
})
case EnumTag.OpenStringEnum(processUnknown) =>
Some(new BlobDecoder[E] {
def decode(blob: Blob): Either[PayloadError, E] = {
val str = blob.toUTF8String
val result: E = values
.find(_.stringValue == str)
.map(_.value)
.getOrElse(processUnknown(str))
Right(result)
}
})
case _ => None
}
}
override def biject[A, B](
schema: Schema[A],
bijection: Bijection[A, B]
): MaybeBlobDecoder[B] =
self(schema).map(_.map(bijection.to))
override def refine[A, B](
schema: Schema[A],
refinement: Refinement[A, B]
): MaybeBlobDecoder[B] =
self(schema).map(decoderA =>
new BlobDecoder[B] {
def decode(blob: Blob): Either[PayloadError, B] =
decoderA
.decode(blob)
.flatMap(refinement.asFunction(_).left.map { error =>
PayloadError(
PayloadPath.root,
refinement.tag.id.show,
error.getMessage
)
})
}
)
override def option[A](
schema: Schema[A]
): MaybeBlobDecoder[Option[A]] =
self(schema).map(decoderA =>
new BlobDecoder[Option[A]] {
def decode(blob: Blob): Either[PayloadError, Option[A]] =
if (blob.isEmpty) Right(None)
else decoderA.decode(blob).map(a => Some(a))
}
)
}
private object StringAndBlobWriterVisitor
extends SchemaVisitor.Default[MaybeBlobEncoder] {
self =>
override def default[A]: MaybeBlobEncoder[A] = None
override def primitive[P](
shapeId: ShapeId,
hints: Hints,
tag: Primitive[P]
): MaybeBlobEncoder[P] = tag match {
case PString => Some(stringEncoder)
case PBlob => Some(blobWriter)
case _ => None
}
override def enumeration[E](
shapeId: ShapeId,
hints: Hints,
tag: EnumTag[E],
values: List[EnumValue[E]],
total: E => EnumValue[E]
): MaybeBlobEncoder[E] = {
tag match {
case EnumTag.ClosedStringEnum =>
Some(stringEncoder.contramap(total(_: E).stringValue))
case EnumTag.OpenStringEnum(_) =>
Some(stringEncoder.contramap(total(_: E).stringValue))
case _ => None
}
}
override def biject[A, B](
schema: Schema[A],
bijection: Bijection[A, B]
): MaybeBlobEncoder[B] =
self(schema).map(_.contramap(bijection.from))
override def refine[A, B](
schema: Schema[A],
refinement: Refinement[A, B]
): MaybeBlobEncoder[B] =
self(schema).map(_.contramap(refinement.from))
override def option[A](
schema: Schema[A]
): MaybeBlobEncoder[Option[A]] =
self(schema).map(writerA =>
new BlobEncoder[Option[A]] {
def encode(maybeA: Option[A]): Blob = maybeA match {
case Some(a) => writerA.encode(a)
case None => Blob.empty
}
}
)
}
private val stringDecoder: BlobDecoder[String] = {
new BlobDecoder[String] {
def decode(blob: Blob): Either[PayloadError, String] = Right(
blob.toUTF8String
)
}
}
private val stringEncoder: PayloadEncoder[String] =
Encoder.lift(Blob(_))
private val blobDecoder: BlobDecoder[Blob] = {
new BlobDecoder[Blob] {
def decode(blob: Blob): Either[PayloadError, Blob] = Right(
blob
)
}
}
private val blobWriter: BlobEncoder[Blob] =
Encoder.lift(identity)
}