11package scynamo
22
3- import cats .data .{EitherNec , NonEmptyChain }
3+ import cats .data .{Chain , EitherNec , NonEmptyChain }
44import cats .syntax .either ._
5- import cats .syntax .parallel ._
65import cats .{Monad , SemigroupK }
7- import scynamo .StackFrame .Index
6+ import scynamo .StackFrame .{ Index , MapKey }
87import scynamo .generic .auto .AutoDerivationUnlocked
98import scynamo .generic .{GenericScynamoDecoder , SemiautoDerivationDecoder }
9+ import scynamo .syntax .attributevalue ._
1010import shapeless .labelled .{field , FieldType }
1111import shapeless .tag .@@
1212import shapeless .{tag , Lazy }
@@ -17,24 +17,24 @@ import java.util.UUID
1717import java .util .concurrent .TimeUnit
1818import scala .annotation .tailrec
1919import scala .collection .compat ._
20+ import scala .collection .immutable .Seq
2021import scala .concurrent .duration .{Duration , FiniteDuration }
21- import scala .jdk .CollectionConverters ._
2222import scala .util .control .NonFatal
2323
2424trait ScynamoDecoder [A ] extends ScynamoDecoderFunctions { self =>
2525 def decode (attributeValue : AttributeValue ): EitherNec [ScynamoDecodeError , A ]
2626
2727 def map [B ](f : A => B ): ScynamoDecoder [B ] =
28- value => decode(value ).map(f)
28+ ScynamoDecoder .instance( decode(_ ).map(f) )
2929
3030 def flatMap [B ](f : A => ScynamoDecoder [B ]): ScynamoDecoder [B ] =
31- value => decode(value).flatMap(f(_).decode(value))
31+ ScynamoDecoder .instance( value => decode(value).flatMap(f(_).decode(value) ))
3232
3333 def orElse [AA >: A ](other : ScynamoDecoder [A ]): ScynamoDecoder [AA ] =
34- value => decode(value).orElse(other.decode(value))
34+ ScynamoDecoder .instance( value => decode(value).orElse(other.decode(value) ))
3535
3636 def transform [B ](f : EitherNec [ScynamoDecodeError , A ] => EitherNec [ScynamoDecodeError , B ]): ScynamoDecoder [B ] =
37- value => f(decode(value))
37+ ScynamoDecoder .instance( value => f(decode(value) ))
3838
3939 def defaultValue : Option [A ] = None
4040
@@ -48,11 +48,14 @@ object ScynamoDecoder extends DefaultScynamoDecoderInstances {
4848 def apply [A ](implicit instance : ScynamoDecoder [A ]): ScynamoDecoder [A ] = instance
4949
5050 def const [A ](value : A ): ScynamoDecoder [A ] =
51- _ => Right (value)
51+ instance(_ => Right (value))
52+
53+ // SAM syntax generates anonymous classes because of non-abstract methods like `defaultValue`.
54+ private [scynamo] def instance [A ](f : AttributeValue => EitherNec [ScynamoDecodeError , A ]): ScynamoDecoder [A ] = f(_)
5255}
5356
5457trait DefaultScynamoDecoderInstances extends ScynamoDecoderFunctions with ScynamoIterableDecoder {
55- import scynamo . syntax . attributevalue . _
58+ private val rightNone = Right ( None )
5659
5760 implicit val catsInstances : Monad [ScynamoDecoder ] with SemigroupK [ScynamoDecoder ] =
5861 new Monad [ScynamoDecoder ] with SemigroupK [ScynamoDecoder ] {
@@ -73,87 +76,97 @@ trait DefaultScynamoDecoderInstances extends ScynamoDecoderFunctions with Scynam
7376 case Left (errors) => Left (errors)
7477 }
7578
76- go(a, _)
79+ ScynamoDecoder .instance( go(a, _) )
7780 }
7881
7982 override def combineK [A ](x : ScynamoDecoder [A ], y : ScynamoDecoder [A ]): ScynamoDecoder [A ] =
8083 x.orElse(y)
8184 }
8285
83- implicit val stringDecoder : ScynamoDecoder [String ] = attributeValue => attributeValue.asEither(ScynamoType .String )
86+ implicit val stringDecoder : ScynamoDecoder [String ] =
87+ ScynamoDecoder .instance(_.asEither(ScynamoType .String ))
8488
8589 implicit val intDecoder : ScynamoDecoder [Int ] =
86- attributeValue => attributeValue. asEither(ScynamoType .Number ).flatMap(s => convert(s , " Int" )(_.toInt))
90+ ScynamoDecoder .instance(_. asEither(ScynamoType .Number ).flatMap(convert(_ , " Int" )(_.toInt) ))
8791
8892 implicit val longDecoder : ScynamoDecoder [Long ] =
89- attributeValue => attributeValue. asEither(ScynamoType .Number ).flatMap(s => convert(s , " Long" )(_.toLong))
93+ ScynamoDecoder .instance(_. asEither(ScynamoType .Number ).flatMap(convert(_ , " Long" )(_.toLong) ))
9094
9195 implicit val bigIntDecoder : ScynamoDecoder [BigInt ] =
92- attributeValue => attributeValue. asEither(ScynamoType .Number ).flatMap(s => convert(s , " BigInt" )(BigInt (_ )))
96+ ScynamoDecoder .instance(_. asEither(ScynamoType .Number ).flatMap(convert(_ , " BigInt" )(BigInt .apply )))
9397
9498 implicit val floatDecoder : ScynamoDecoder [Float ] =
95- attributeValue => attributeValue. asEither(ScynamoType .Number ).flatMap(s => convert(s , " Float" )(_.toFloat))
99+ ScynamoDecoder .instance(_. asEither(ScynamoType .Number ).flatMap(convert(_ , " Float" )(_.toFloat) ))
96100
97101 implicit val doubleDecoder : ScynamoDecoder [Double ] =
98- attributeValue => attributeValue. asEither(ScynamoType .Number ).flatMap(s => convert(s , " Double" )(_.toDouble))
102+ ScynamoDecoder .instance(_. asEither(ScynamoType .Number ).flatMap(convert(_ , " Double" )(_.toDouble) ))
99103
100104 implicit val bigDecimalDecoder : ScynamoDecoder [BigDecimal ] =
101- attributeValue => attributeValue. asEither(ScynamoType .Number ).flatMap(s => convert(s , " BigDecimal" )(BigDecimal (_ )))
105+ ScynamoDecoder .instance(_. asEither(ScynamoType .Number ).flatMap(convert(_ , " BigDecimal" )(BigDecimal .apply )))
102106
103- implicit val booleanDecoder : ScynamoDecoder [Boolean ] = attributeValue => attributeValue.asEither(ScynamoType .Bool )
107+ implicit val booleanDecoder : ScynamoDecoder [Boolean ] =
108+ ScynamoDecoder .instance(_.asEither(ScynamoType .Bool ))
104109
105110 implicit val instantDecoder : ScynamoDecoder [Instant ] =
106- attributeValue =>
111+ ScynamoDecoder .instance { attr =>
107112 for {
108- nstring <- attributeValue .asEither(ScynamoType .Number )
109- result <- convert(nstring , " Long" )(_.toLong)
113+ number <- attr .asEither(ScynamoType .Number )
114+ result <- convert(number , " Long" )(_.toLong)
110115 } yield Instant .ofEpochMilli(result)
116+ }
111117
112118 implicit val instantTtlDecoder : ScynamoDecoder [Instant @@ TimeToLive ] =
113- attributeValue =>
119+ ScynamoDecoder .instance { attr =>
114120 for {
115- nstring <- attributeValue .asEither(ScynamoType .Number )
116- result <- convert(nstring , " Long" )(_.toLong)
121+ number <- attr .asEither(ScynamoType .Number )
122+ result <- convert(number , " Long" )(_.toLong)
117123 } yield tag[TimeToLive ][Instant ](Instant .ofEpochSecond(result))
124+ }
118125
119- implicit def seqDecoder [A : ScynamoDecoder ]: ScynamoDecoder [scala.collection.immutable.Seq [A ]] = iterableDecoder
120-
121- implicit def listDecoder [A : ScynamoDecoder ]: ScynamoDecoder [List [A ]] = iterableDecoder
122-
126+ implicit def seqDecoder [A : ScynamoDecoder ]: ScynamoDecoder [Seq [A ]] = iterableDecoder
127+ implicit def listDecoder [A : ScynamoDecoder ]: ScynamoDecoder [List [A ]] = iterableDecoder
123128 implicit def vectorDecoder [A : ScynamoDecoder ]: ScynamoDecoder [Vector [A ]] = iterableDecoder
129+ implicit def setDecoder [A : ScynamoDecoder ]: ScynamoDecoder [Set [A ]] = iterableDecoder
124130
125- implicit def setDecoder [A : ScynamoDecoder ]: ScynamoDecoder [Set [A ]] = iterableDecoder
126-
127- implicit def optionDecoder [A : ScynamoDecoder ]: ScynamoDecoder [Option [A ]] =
131+ implicit def optionDecoder [A ](implicit element : ScynamoDecoder [A ]): ScynamoDecoder [Option [A ]] =
128132 new ScynamoDecoder [Option [A ]] {
133+ override val defaultValue : Option [Option [A ]] = Some (None )
129134 override def decode (attributeValue : AttributeValue ): EitherNec [ScynamoDecodeError , Option [A ]] =
130- if (attributeValue.nul()) Right (None ) else ScynamoDecoder [A ].decode(attributeValue).map(Some (_))
131-
132- override def defaultValue : Option [Option [A ]] = Some (None )
135+ if (attributeValue.nul) rightNone else element.decode(attributeValue).map(Some .apply)
133136 }
134137
135- implicit val finiteDurationDecoder : ScynamoDecoder [FiniteDuration ] = longDecoder.map(Duration .fromNanos)
136-
137- implicit val durationDecoder : ScynamoDecoder [Duration ] = longDecoder.map(n => Duration (n, TimeUnit .NANOSECONDS ))
138-
139- implicit val uuidDecoder : ScynamoDecoder [UUID ] = attributeValue =>
140- attributeValue.asEither(ScynamoType .String ).flatMap(s => convert(s, " UUID" )(UUID .fromString))
141-
142- implicit def mapDecoder [A , B ](implicit
143- keyDecoder : ScynamoKeyDecoder [A ],
144- valueDecoder : ScynamoDecoder [B ]
145- ): ScynamoDecoder [Map [A , B ]] =
146- attributeValue =>
147- attributeValue.asEither(ScynamoType .Map ).flatMap { javaMap =>
148- javaMap.asScala.toVector.zipWithIndex
149- .parTraverse { case ((key, value), i) =>
150- (keyDecoder.decode(key), valueDecoder.decode(value)).parMapN(_ -> _).leftMap(_.map(_.push(Index (i))))
151- }
152- .map(_.toMap)
138+ implicit val finiteDurationDecoder : ScynamoDecoder [FiniteDuration ] =
139+ longDecoder.map(Duration .fromNanos)
140+
141+ implicit val durationDecoder : ScynamoDecoder [Duration ] =
142+ longDecoder.map(Duration (_, TimeUnit .NANOSECONDS ))
143+
144+ implicit val uuidDecoder : ScynamoDecoder [UUID ] =
145+ ScynamoDecoder .instance(_.asEither(ScynamoType .String ).flatMap(convert(_, " UUID" )(UUID .fromString)))
146+
147+ implicit def mapDecoder [A , B ](implicit key : ScynamoKeyDecoder [A ], value : ScynamoDecoder [B ]): ScynamoDecoder [Map [A , B ]] =
148+ ScynamoDecoder .instance(_.asEither(ScynamoType .Map ).flatMap { attributes =>
149+ var allErrors = Chain .empty[ScynamoDecodeError ]
150+ val allValues = Map .newBuilder[A , B ]
151+
152+ attributes.forEach { (k, v) =>
153+ (key.decode(k), value.decode(v)) match {
154+ case (Right (k), Right (v)) =>
155+ allValues += k -> v
156+ case (Left (errors), Right (_)) =>
157+ allErrors ++= StackFrame .decoding(errors, MapKey (k)).toChain
158+ case (Right (_), Left (errors)) =>
159+ allErrors ++= StackFrame .decoding(errors, MapKey (k)).toChain
160+ case (Left (kErrors), Left (vErrors)) =>
161+ allErrors ++= StackFrame .decoding(kErrors ++ vErrors, MapKey (k)).toChain
162+ }
153163 }
154164
165+ NonEmptyChain .fromChain(allErrors).toLeft(allValues.result())
166+ })
167+
155168 implicit val attributeValueDecoder : ScynamoDecoder [AttributeValue ] =
156- attributeValue => Right (attributeValue )
169+ ScynamoDecoder .instance( Right .apply )
157170
158171 implicit def fieldDecoder [K , V ](implicit V : Lazy [ScynamoDecoder [V ]]): ScynamoDecoder [FieldType [K , V ]] =
159172 new ScynamoDecoder [FieldType [K , V ]] {
@@ -166,21 +179,24 @@ trait DefaultScynamoDecoderInstances extends ScynamoDecoderFunctions with Scynam
166179
167180trait ScynamoIterableDecoder extends LowestPrioAutoDecoder {
168181 import scynamo .syntax .attributevalue ._
169- def iterableDecoder [ A : ScynamoDecoder , C [_] <: Iterable [ A ], X ]( implicit factory : Factory [ A , C [ A ]]) : ScynamoDecoder [ C [ A ]] =
170- attributeValue =>
171- attributeValue. asEither(ScynamoType .List ).flatMap { theList =>
172- val builder = factory.newBuilder
173- var elems = Either .rightNec[ ScynamoDecodeError , builder. type ](builder)
174- var i = 0
175-
176- theList.forEach { elem =>
177- val decoded = ScynamoDecoder [ A ] .decode(elem).leftMap(_.map(_.push( Index ( i))))
178- elems = (elems, decoded).parMapN((builder, dec) => builder += dec)
179- i += 1
182+
183+ def iterableDecoder [ A , C [x] <: Iterable [x]]( implicit element : ScynamoDecoder [ A ], factory : Factory [ A , C [ A ]]) : ScynamoDecoder [ C [ A ]] =
184+ ScynamoDecoder .instance(_. asEither(ScynamoType .List ).flatMap { attributes =>
185+ var allErrors = Chain .empty[ ScynamoDecodeError ]
186+ val allValues = factory.newBuilder
187+ var i = 0
188+
189+ while (i < attributes.size()) {
190+ element .decode(attributes.get( i)) match {
191+ case Right (value) => allValues += value
192+ case Left (errors) => allErrors ++= StackFrame .decoding(errors, Index (i)).toChain
180193 }
181194
182- elems.map(_.result())
195+ i += 1
183196 }
197+
198+ NonEmptyChain .fromChain(allErrors).toLeft(allValues.result())
199+ })
184200}
185201
186202trait LowestPrioAutoDecoder {
@@ -209,17 +225,22 @@ trait ObjectScynamoDecoder[A] extends ScynamoDecoder[A] {
209225 attributeValue.asEither(ScynamoType .Map ).flatMap(decodeMap)
210226
211227 override def map [B ](f : A => B ): ObjectScynamoDecoder [B ] =
212- attributes => decodeMap(attributes ).map(f)
228+ ObjectScynamoDecoder .instance( decodeMap(_ ).map(f) )
213229
214230 override def transform [B ](f : EitherNec [ScynamoDecodeError , A ] => EitherNec [ScynamoDecodeError , B ]): ObjectScynamoDecoder [B ] =
215- attributes => f(decodeMap(attributes))
231+ ObjectScynamoDecoder .instance( attributes => f(decodeMap(attributes) ))
216232}
217233
218234object ObjectScynamoDecoder extends ScynamoDecoderFunctions with SemiautoDerivationDecoder {
219235 def apply [A ](implicit instance : ObjectScynamoDecoder [A ]): ObjectScynamoDecoder [A ] = instance
220236
237+ // SAM syntax generates anonymous classes because of non-abstract methods like `defaultValue`.
238+ private [scynamo] def instance [A ](
239+ f : java.util.Map [String , AttributeValue ] => EitherNec [ScynamoDecodeError , A ]
240+ ): ObjectScynamoDecoder [A ] = f(_)
241+
221242 def const [A ](value : A ): ObjectScynamoDecoder [A ] =
222- _ => Right (value)
243+ instance( _ => Right (value) )
223244
224245 implicit val catsInstances : Monad [ObjectScynamoDecoder ] with SemigroupK [ObjectScynamoDecoder ] =
225246 new Monad [ObjectScynamoDecoder ] with SemigroupK [ObjectScynamoDecoder ] {
@@ -230,7 +251,7 @@ object ObjectScynamoDecoder extends ScynamoDecoderFunctions with SemiautoDerivat
230251 ObjectScynamoDecoder .const(x)
231252
232253 override def flatMap [A , B ](fa : ObjectScynamoDecoder [A ])(f : A => ObjectScynamoDecoder [B ]): ObjectScynamoDecoder [B ] =
233- attributes => fa.decodeMap(attributes).flatMap(f(_).decodeMap(attributes))
254+ instance( attributes => fa.decodeMap(attributes).flatMap(f(_).decodeMap(attributes) ))
234255
235256 override def tailRecM [A , B ](a : A )(f : A => ObjectScynamoDecoder [Either [A , B ]]): ObjectScynamoDecoder [B ] = {
236257 @ tailrec def go (a : A , attributes : java.util.Map [String , AttributeValue ]): EitherNec [ScynamoDecodeError , B ] =
@@ -240,15 +261,27 @@ object ObjectScynamoDecoder extends ScynamoDecoderFunctions with SemiautoDerivat
240261 case Left (errors) => Left (errors)
241262 }
242263
243- go(a, _)
264+ instance( go(a, _) )
244265 }
245266
246267 override def combineK [A ](x : ObjectScynamoDecoder [A ], y : ObjectScynamoDecoder [A ]): ObjectScynamoDecoder [A ] =
247- attributes => x.decodeMap(attributes).orElse(y.decodeMap(attributes))
268+ instance( attributes => x.decodeMap(attributes).orElse(y.decodeMap(attributes) ))
248269 }
249270
250- implicit def mapDecoder [A ](implicit valueDecoder : ScynamoDecoder [A ]): ObjectScynamoDecoder [Map [String , A ]] =
251- javaMap => javaMap.asScala.toVector.parTraverse { case (key, value) => valueDecoder.decode(value).map(key -> _) }.map(_.toMap)
271+ implicit def mapDecoder [A ](implicit value : ScynamoDecoder [A ]): ObjectScynamoDecoder [Map [String , A ]] =
272+ instance { attributes =>
273+ var allErrors = Chain .empty[ScynamoDecodeError ]
274+ val allValues = Map .newBuilder[String , A ]
275+
276+ attributes.forEach { (k, v) =>
277+ value.decode(v) match {
278+ case Right (value) => allValues += k -> value
279+ case Left (errors) => allErrors ++= StackFrame .decoding(errors, MapKey (k)).toChain
280+ }
281+ }
282+
283+ NonEmptyChain .fromChain(allErrors).toLeft(allValues.result())
284+ }
252285}
253286
254287trait ScynamoKeyDecoder [A ] {
@@ -258,11 +291,12 @@ trait ScynamoKeyDecoder[A] {
258291object ScynamoKeyDecoder {
259292 def apply [A ](implicit decoder : ScynamoKeyDecoder [A ]): ScynamoKeyDecoder [A ] = decoder
260293
261- implicit val stringKeyDecoder : ScynamoKeyDecoder [String ] = s => Right (s)
294+ // SAM syntax generates anonymous classes because of non-abstract methods like `defaultValue`.
295+ private [scynamo] def instance [A ](f : String => EitherNec [ScynamoDecodeError , A ]): ScynamoKeyDecoder [A ] = f(_)
262296
263- implicit val uuidKeyDecoder : ScynamoKeyDecoder [UUID ] = s => {
264- val result = Either .catchOnly[ IllegalArgumentException ]( UUID .fromString(s) )
297+ implicit val stringKeyDecoder : ScynamoKeyDecoder [String ] =
298+ instance( Right .apply )
265299
266- result.leftMap(e => NonEmptyChain .one( ScynamoDecodeError .conversionError( s " $s " , " UUID" , Some (e))))
267- }
300+ implicit val uuidKeyDecoder : ScynamoKeyDecoder [ UUID ] =
301+ instance( ScynamoDecoder .convert(_, " UUID " )( UUID .fromString))
268302}
0 commit comments