11package scynamo
22
33import cats .data .{Chain , EitherNec , NonEmptyChain }
4- import cats .syntax .either ._
4+ import cats .syntax .all ._
55import cats .{Monad , SemigroupK }
66import scynamo .StackFrame .{Index , MapKey }
77import scynamo .generic .auto .AutoDerivationUnlocked
88import scynamo .generic .{GenericScynamoDecoder , SemiautoDerivationDecoder }
99import scynamo .syntax .attributevalue ._
10- import scynamo .wrapper .YearMonthFormatter . yearMonthFormatter
10+ import scynamo .wrapper .DateTimeFormatters
1111import shapeless .labelled .{field , FieldType }
1212import shapeless .tag .@@
1313import shapeless .{tag , Lazy }
1414import software .amazon .awssdk .services .dynamodb .model .AttributeValue
1515
16- import java .time .{ Instant , YearMonth }
16+ import java .time ._
1717import java .util .UUID
18- import java .util .concurrent .TimeUnit
1918import scala .annotation .tailrec
2019import scala .collection .compat ._
2120import scala .collection .immutable .Seq
@@ -43,6 +42,9 @@ trait ScynamoDecoder[A] extends ScynamoDecoderFunctions { self =>
4342 override def decode (attributeValue : AttributeValue ): EitherNec [ScynamoDecodeError , A ] = self.decode(attributeValue)
4443 override val defaultValue : Option [A ] = Some (value)
4544 }
45+
46+ def emap [B ](f : A => EitherNec [ScynamoDecodeError , B ]): ScynamoDecoder [B ] =
47+ ScynamoDecoder .instance(decode(_).flatMap(f))
4648}
4749
4850object ScynamoDecoder extends DefaultScynamoDecoderInstances {
@@ -51,8 +53,8 @@ object ScynamoDecoder extends DefaultScynamoDecoderInstances {
5153 def const [A ](value : A ): ScynamoDecoder [A ] =
5254 instance(_ => Right (value))
5355
54- // SAM syntax generates anonymous classes because of non-abstract methods like `defaultValue`.
55- private [scynamo] def instance [A ](f : AttributeValue => EitherNec [ScynamoDecodeError , A ]): ScynamoDecoder [A ] = f (_)
56+ /** SAM syntax generates anonymous classes on Scala 2 because of non-abstract methods like `defaultValue`. */
57+ def instance [A ](decode : AttributeValue => EitherNec [ScynamoDecodeError , A ]): ScynamoDecoder [A ] = decode (_)
5658}
5759
5860trait DefaultScynamoDecoderInstances extends ScynamoDecoderFunctions with ScynamoIterableDecoder {
@@ -109,20 +111,10 @@ trait DefaultScynamoDecoderInstances extends ScynamoDecoderFunctions with Scynam
109111 ScynamoDecoder .instance(_.asEither(ScynamoType .Bool ))
110112
111113 implicit val instantDecoder : ScynamoDecoder [Instant ] =
112- ScynamoDecoder .instance { attr =>
113- for {
114- number <- attr.asEither(ScynamoType .Number )
115- result <- convert(number, " Long" )(_.toLong)
116- } yield Instant .ofEpochMilli(result)
117- }
114+ longDecoder.map(Instant .ofEpochMilli)
118115
119116 implicit val instantTtlDecoder : ScynamoDecoder [Instant @@ TimeToLive ] =
120- ScynamoDecoder .instance { attr =>
121- for {
122- number <- attr.asEither(ScynamoType .Number )
123- result <- convert(number, " Long" )(_.toLong)
124- } yield tag[TimeToLive ][Instant ](Instant .ofEpochSecond(result))
125- }
117+ longDecoder.map(seconds => tag[TimeToLive ](Instant .ofEpochSecond(seconds)))
126118
127119 implicit def seqDecoder [A : ScynamoDecoder ]: ScynamoDecoder [Seq [A ]] = iterableDecoder
128120 implicit def listDecoder [A : ScynamoDecoder ]: ScynamoDecoder [List [A ]] = iterableDecoder
@@ -140,13 +132,25 @@ trait DefaultScynamoDecoderInstances extends ScynamoDecoderFunctions with Scynam
140132 longDecoder.map(Duration .fromNanos)
141133
142134 implicit val durationDecoder : ScynamoDecoder [Duration ] =
143- longDecoder.map(Duration (_, TimeUnit .NANOSECONDS ))
135+ finiteDurationDecoder.widen
136+
137+ implicit val javaDurationDecoder : ScynamoDecoder [java.time.Duration ] =
138+ longDecoder.map(java.time.Duration .ofNanos)
144139
145140 implicit val yearMonthDecoder : ScynamoDecoder [YearMonth ] =
146- ScynamoDecoder .instance(_.asEither(ScynamoType .String ).flatMap(convert(_, " YearMonth" )(YearMonth .parse(_, yearMonthFormatter))))
141+ stringDecoder.emap(convert(_, " YearMonth" )(YearMonth .parse(_, DateTimeFormatters .yearMonth)))
142+
143+ implicit val localDateDecoder : ScynamoDecoder [LocalDate ] =
144+ stringDecoder.emap(convert(_, " LocalDate" )(LocalDate .parse(_, DateTimeFormatters .localDate)))
145+
146+ implicit val localDateTimeDecoder : ScynamoDecoder [LocalDateTime ] =
147+ stringDecoder.emap(convert(_, " LocalDateTime" )(LocalDateTime .parse(_, DateTimeFormatters .localDateTime)))
148+
149+ implicit val zonedDateTimeDecoder : ScynamoDecoder [ZonedDateTime ] =
150+ stringDecoder.emap(convert(_, " ZonedDateTime" )(ZonedDateTime .parse(_, DateTimeFormatters .zonedDateTime)))
147151
148152 implicit val uuidDecoder : ScynamoDecoder [UUID ] =
149- ScynamoDecoder .instance(_.asEither( ScynamoType . String ).flatMap( convert(_, " UUID" )(UUID .fromString) ))
153+ stringDecoder.emap( convert(_, " UUID" )(UUID .fromString))
150154
151155 implicit def mapDecoder [A , B ](implicit key : ScynamoKeyDecoder [A ], value : ScynamoDecoder [B ]): ScynamoDecoder [Map [A , B ]] =
152156 ScynamoDecoder .instance(_.asEither(ScynamoType .Map ).flatMap { attributes =>
0 commit comments