@@ -30,6 +30,10 @@ import scala.util.Try
3030sealed trait DefType {
3131 def className : Option [String ]
3232
33+ def description : Option [String ] = None
34+
35+ def describe (desc : String ): DefType = DefType .Described (this , Some (desc))
36+
3337 def isOpt : Boolean = false
3438
3539 def isNull : Boolean = false
@@ -42,8 +46,13 @@ sealed trait DefType {
4246
4347 protected def template (path : JsonPath , config : TemplateConfig ): Json
4448
45- def merge (that : DefType ): DefType =
46- if (this == that) {
49+ def merge (that : DefType ): DefType = {
50+ // Unwrap Described for comparison, descriptions are not preserved through merge
51+ val unwrappedThis = this match { case DefType .Described (dt, _) => dt; case _ => this }
52+ val unwrappedThat = that match { case DefType .Described (dt, _) => dt; case _ => that }
53+ if (unwrappedThis != this || unwrappedThat != that) {
54+ unwrappedThis.merge(unwrappedThat)
55+ } else if (this == that) {
4756 this
4857 } else if (this .isNull) {
4958 that.opt
@@ -60,35 +69,45 @@ sealed trait DefType {
6069 } else {
6170 throw new RuntimeException (s " Incompatible typed: \n $this\n\n $that" )
6271 }
72+ }
6373}
6474
6575object DefType {
6676 implicit def rw : RW [DefType ] = RW .from[DefType ](r = dt2V, w = v2dt, d = DefType .Json )
6777
78+ private def withDesc (base : fabric.Obj , desc : Option [String ]): fabric.Obj = desc match {
79+ case Some (d) => base.merge(fabric.Obj (" description" -> str(d))).asObj
80+ case None => base
81+ }
82+
6883 private def dt2V (dt : DefType ): Json = dt match {
69- case Obj (map, cn) => obj(
84+ case Described (inner, desc) => withDesc(dt2V(inner).asObj, desc)
85+ case Obj (map, cn, desc) => withDesc(obj(
7086 " type" -> str(" object" ),
7187 " values" -> fabric.Obj (map.map { case (key, dt) => key -> dt2V(dt) }),
7288 " className" -> cn.json
73- )
74- case Arr (t) => obj(" type" -> str(" array" ), " value" -> dt2V(t))
75- case Opt (t) => obj(" type" -> str(" optional" ), " value" -> dt2V(t))
89+ ), desc)
90+ case Arr (t, desc ) => withDesc( obj(" type" -> str(" array" ), " value" -> dt2V(t)), desc )
91+ case Opt (t, desc ) => withDesc( obj(" type" -> str(" optional" ), " value" -> dt2V(t)), desc )
7692 case Str => obj(" type" -> str(" string" ))
7793 case Int => obj(" type" -> str(" numeric" ), " precision" -> str(" integer" ))
7894 case Dec => obj(" type" -> str(" numeric" ), " precision" -> str(" decimal" ))
7995 case Bool => obj(" type" -> str(" boolean" ))
80- case Enum (values, cn) => obj(" type" -> str(" enum" ), " values" -> values, " className" -> cn.json)
81- case Poly (values, cn) => obj(
96+ case Enum (values, cn, desc ) => withDesc( obj(" type" -> str(" enum" ), " values" -> values, " className" -> cn.json), desc )
97+ case Poly (values, cn, desc ) => withDesc( obj(
8298 " type" -> str(" poly" ),
8399 " values" -> values.map { case (key, dt) => key -> dt2V(dt) },
84100 " className" -> cn.json
85- )
101+ ), desc)
86102 case Json => obj(" type" -> str(" json" ))
87103 case Null => obj(" type" -> str(" null" ))
88104 }
89105
106+ private def readDesc (o : fabric.Obj ): Option [String ] = o.get(" description" ).map(_.asString)
107+
90108 private def v2dt (v : Json ): DefType = {
91109 val o = v.asObj
110+ val desc = readDesc(o)
92111 o.value(" type" ).asString match {
93112 case " object" =>
94113 val map : Map [String , Json ] = o.value
@@ -100,30 +119,31 @@ object DefType {
100119 case fabric.Null => None
101120 case s : Str => Some (s.value)
102121 case j => throw new RuntimeException (s " Unsupported className value: $j" )
103- }
104- )
105- case " array" => Arr (
106- t = v2dt(o.value(" value" ))
122+ },
123+ description = desc
107124 )
108- case " optional" => Opt (v2dt(o.value(" value" )))
109- case " string" => Str
125+ case " array" => Arr (t = v2dt(o.value(" value" )), description = desc)
126+ case " optional" => Opt (v2dt(o.value(" value" )), description = desc)
127+ case " string" => desc.fold[DefType ](Str )(Str .describe)
110128 case " numeric" => o.value(" precision" ).asString match {
111- case " integer" => Int
112- case " decimal" => Dec
129+ case " integer" => desc.fold[ DefType ]( Int )( Int .describe)
130+ case " decimal" => desc.fold[ DefType ]( Dec )( Dec .describe)
113131 }
114- case " boolean" => Bool
115- case " enum" => Enum (o.value(" values" ).asVector.toList, o.get(" className" ).map(_.asString))
132+ case " boolean" => desc.fold[ DefType ]( Bool )( Bool .describe)
133+ case " enum" => Enum (o.value(" values" ).asVector.toList, o.get(" className" ).map(_.asString), description = desc )
116134 case " poly" =>
117- Poly (o.value(" values" ).asMap.map { case (key, json) => key -> v2dt(json) }, o.get(" className" ).map(_.asString))
118- case " json" => Json
119- case " null" => Null
135+ Poly (o.value(" values" ).asMap.map { case (key, json) => key -> v2dt(json) }, o.get(" className" ).map(_.asString), description = desc )
136+ case " json" => desc.fold[ DefType ]( Json )( Json .describe)
137+ case " null" => desc.fold[ DefType ]( Null )( Null .describe)
120138 }
121139 }
122140
123- case class Obj (map : Map [String , DefType ], className : Option [String ]) extends DefType {
141+ case class Obj (map : Map [String , DefType ], className : Option [String ], override val description : Option [String ] = None ) extends DefType {
142+ override def describe (desc : String ): Obj = copy(description = Some (desc))
143+
124144 override def merge (that : DefType ): DefType = that match {
125- case Obj (thatMap, cn) => Obj (mergeMap(map, thatMap), cn)
126- case Opt (Obj (thatMap, cn) ) => Opt (Obj (mergeMap(map, thatMap), cn))
145+ case Obj (thatMap, cn, _ ) => Obj (mergeMap(map, thatMap), cn)
146+ case Opt (Obj (thatMap, cn, _), _ ) => Opt (Obj (mergeMap(map, thatMap), cn))
127147 case _ => super .merge(that)
128148 }
129149
@@ -142,11 +162,12 @@ object DefType {
142162 object Obj {
143163 def apply (className : Option [String ], entries : (String , DefType )* ): Obj = Obj (VectorMap (entries* ), className)
144164 }
145- case class Arr (t : DefType ) extends DefType {
165+ case class Arr (t : DefType , override val description : Option [ String ] = None ) extends DefType {
146166 override def className : Option [String ] = None
167+ override def describe (desc : String ): Arr = copy(description = Some (desc))
147168
148169 override def merge (that : DefType ): DefType = that match {
149- case Arr (thatType) => Arr (t.merge(thatType))
170+ case Arr (thatType, _ ) => Arr (t.merge(thatType))
150171 case Null => this
151172 case _ => super .merge(that)
152173 }
@@ -157,13 +178,14 @@ object DefType {
157178 t.template(path \ 2 , config)
158179 )
159180 }
160- case class Opt (t : DefType ) extends DefType {
181+ case class Opt (t : DefType , override val description : Option [ String ] = None ) extends DefType {
161182 override def className : Option [String ] = Some (" scala.Option" )
162183 override def isOpt : Boolean = true
163184 override def opt : DefType = this
185+ override def describe (desc : String ): Opt = copy(description = Some (desc))
164186
165187 override def merge (that : DefType ): DefType = that match {
166- case Opt (thatOpt) => t.merge(thatOpt) match {
188+ case Opt (thatOpt, _ ) => t.merge(thatOpt) match {
167189 case o : Opt => o
168190 case result => Opt (result)
169191 }
@@ -211,12 +233,23 @@ object DefType {
211233
212234 override protected def template (path : JsonPath , config : TemplateConfig ): Json = config.json(path)
213235 }
214- case class Enum (values : List [Json ], className : Option [String ]) extends DefType {
236+ case class Enum (values : List [Json ], className : Option [String ], override val description : Option [String ] = None ) extends DefType {
237+ override def describe (desc : String ): Enum = copy(description = Some (desc))
215238 override protected def template (path : JsonPath , config : TemplateConfig ): Json = config.`enum`(path, values)
216239 }
217- case class Poly (values : Map [String , DefType ], className : Option [String ]) extends DefType {
240+ case class Poly (values : Map [String , DefType ], className : Option [String ], override val description : Option [String ] = None ) extends DefType {
241+ override def describe (desc : String ): Poly = copy(description = Some (desc))
218242 override protected def template (path : JsonPath , config : TemplateConfig ): Json = values.head._2.template(path, config)
219243 }
244+ case class Described (dt : DefType , override val description : Option [String ]) extends DefType {
245+ override def className : Option [String ] = dt.className
246+ override def isOpt : Boolean = dt.isOpt
247+ override def isNull : Boolean = dt.isNull
248+ override def opt : DefType = Described (dt.opt, description)
249+ override def describe (desc : String ): Described = copy(description = Some (desc))
250+ override def merge (that : DefType ): DefType = dt.merge(that)
251+ override protected def template (path : JsonPath , config : TemplateConfig ): Json = dt.template(path, config)
252+ }
220253 case object Null extends DefType {
221254 override def className : Option [String ] = None
222255
0 commit comments