1
1
package org .bykn .bosatsu .jsui
2
2
3
3
import scala .concurrent .duration .Duration
4
+ import io .circe .{Json , Encoder , Decoder }
5
+ import io .circe .syntax ._
6
+ import io .circe .parser .decode
7
+
8
+ import cats .syntax .all ._
4
9
5
10
sealed trait State
6
11
@@ -10,16 +15,93 @@ object State {
10
15
}
11
16
case object Init extends State
12
17
case class WithText (
13
- editorText : String
18
+ editorText : String
14
19
) extends HasText
15
20
16
21
case class Compiling (previousState : HasText ) extends State
17
22
18
23
case class Compiled (
19
- editorText : String ,
20
- output : String ,
21
- compilationTime : Duration
24
+ editorText : String ,
25
+ output : String ,
26
+ compilationTime : Duration
22
27
) extends HasText
23
28
24
29
def init : State = Init
25
- }
30
+
31
+ // Custom encoder for Duration to handle it as milliseconds
32
+ implicit val encodeDuration : Encoder [Duration ] =
33
+ Encoder .instance(duration => Json .fromLong(duration.toNanos))
34
+
35
+ // Custom decoder for Duration from milliseconds
36
+ implicit val decodeDuration : Decoder [Duration ] =
37
+ Decoder .instance(cursor => cursor.as[Long ].map(Duration .fromNanos(_)))
38
+ // Encoder for the State trait
39
+ implicit val encodeState : Encoder [State ] = Encoder .instance {
40
+ case Init => Json .obj(" type" -> Json .fromString(" Init" ))
41
+ case wt : WithText => wt.asJson(encodeWithText)
42
+ case compiling : Compiling => compiling.asJson(encodeCompiling)
43
+ case compiled : Compiled => compiled.asJson(encodeCompiled)
44
+ }
45
+
46
+ // Decoders for HasText and its subtypes
47
+ implicit val decodeHasText : Decoder [HasText ] = {
48
+ val decodeWithText : Decoder [WithText ] = Decoder .instance { cursor =>
49
+ cursor.downField(" editorText" ).as[String ].map(WithText (_))
50
+ }
51
+ val decodeCompiled : Decoder [Compiled ] = Decoder .instance { cursor =>
52
+ (
53
+ cursor.downField(" editorText" ).as[String ],
54
+ cursor.downField(" output" ).as[String ],
55
+ cursor.downField(" compilationTime" ).as[Duration ]
56
+ ).mapN(Compiled (_, _, _))
57
+ }
58
+ Decoder .instance { cursor =>
59
+ cursor.downField(" type" ).as[String ].flatMap {
60
+ case " WithText" => cursor.as(decodeWithText)
61
+ case " Compiled" => cursor.as(decodeCompiled)
62
+ }
63
+ }
64
+ }
65
+
66
+ // Decoders for the State trait and its implementations
67
+ implicit val decodeState : Decoder [State ] = Decoder .instance { cursor =>
68
+ cursor.downField(" type" ).as[String ].flatMap {
69
+ case " Init" => Right (Init )
70
+ case " Compiling" =>
71
+ cursor.downField(" previousState" ).as[HasText ].map(Compiling (_))
72
+ case _ => decodeHasText(cursor)
73
+ }
74
+ }
75
+
76
+ // Manual encoders for distinguishing types
77
+ implicit val encodeWithText : Encoder [WithText ] =
78
+ Encoder .forProduct2(" type" , " editorText" ) { wt =>
79
+ (" WithText" , wt.editorText)
80
+ }
81
+
82
+ implicit val encodeCompiled : Encoder [Compiled ] =
83
+ Encoder .forProduct4(" type" , " editorText" , " output" , " compilationTime" ) {
84
+ compiled =>
85
+ (
86
+ " Compiled" ,
87
+ compiled.editorText,
88
+ compiled.output,
89
+ compiled.compilationTime
90
+ )
91
+ }
92
+
93
+ implicit val encodeCompiling : Encoder [Compiling ] =
94
+ Encoder .forProduct2(" type" , " previousState" )(compiling =>
95
+ (
96
+ " Compiling" ,
97
+ compiling.previousState match {
98
+ case comp @ Compiled (_, _, _) => encodeCompiled(comp)
99
+ case wt @ WithText (_) => encodeWithText(wt)
100
+ }
101
+ )
102
+ )
103
+
104
+ def stateToJsonString (s : State ): String = s.asJson.spaces2SortKeys
105
+ def stringToState (str : String ): Either [Throwable , State ] =
106
+ decode[State ](str)
107
+ }
0 commit comments