1
1
package org .scalafmt .config
2
2
3
3
import scala .language .reflectiveCalls
4
-
5
4
import metaconfig ._
6
-
7
5
import java .io .File
8
-
9
6
import metaconfig .Conf
10
7
import metaconfig .ConfError
11
8
import metaconfig .Configured
12
9
import metaconfig .Configured .Ok
13
10
import org .scalafmt .config .PlatformConfig ._
11
+ import org .typelevel .paiges .Doc ._
12
+ import org .typelevel .paiges .Doc
14
13
15
14
object Config {
16
15
@@ -22,45 +21,113 @@ object Config {
22
21
}
23
22
}
24
23
25
- def toHocon [T : ConfEncoder ](value : T ): String = {
26
- val conf = ConfEncoder [T ].write(value)
27
- val out = new StringBuilder
28
- def loop (c : Conf ): Unit = c match {
29
- case Conf .Null () => out.append(" null" ).append(" \n " )
30
- case Conf .Num (num) => out.append(num).append(" \n " )
31
- case Conf .Str (str) => out.append('"' ).append(str).append('"' ).append(" \n " )
32
- case Conf .Bool (bool) => out.append(bool).append(" \n " )
33
- case Conf .Lst (lst) =>
34
- out.append(" [\n " )
35
- lst.foreach { elem =>
36
- out.append(" " )
37
- loop(elem)
38
- }
39
- out.append(" ]\n " )
40
- case Conf .Obj (obj) =>
41
- obj.foreach {
42
- case (key, value) =>
43
- out.append(key).append(" = " )
44
- loop(value)
45
- }
46
- }
24
+ def quote (key : String ): String =
25
+ if (key.indexOf('.' ) < 0 ) key
26
+ else " \" " + key + " \" "
27
+
28
+ val isForbiddenCharacter = Set [Char ](
29
+ '$' , '"' , '{' , '}' , '[' , ']' , ':' , '=' , ',' , '+' , '#' , '`' , '^' , '?' , '!' ,
30
+ '@' , '*' , '&' , ' ' , '\\ '
31
+ )
32
+ val quote = char('"' )
33
+ def quoteKey (key : String ): Doc =
34
+ if (key.indexOf('.' ) < 0 ) text(key)
35
+ else quote + text(key) + quote
36
+
37
+ // Spec is here:
38
+ // https://github.com/lightbend/config/blob/master/HOCON.md#unquoted-strings
39
+ // but this method is conservative and quotes if the string contains non-letter characters
40
+ def needsQuote (str : String ): Boolean =
41
+ str.isEmpty ||
42
+ str.startsWith(" true" ) ||
43
+ str.startsWith(" false" ) ||
44
+ str.startsWith(" null" ) ||
45
+ str.exists(! _.isLetter)
46
+
47
+ def quoteString (str : String ): Doc =
48
+ if (needsQuote(str)) quote + text(str) + quote
49
+ else text(str)
50
+
51
+ def wrap (open : Char , close : Char , doc : Doc ): Doc = {
52
+ (char(open) + line + doc).nested(2 ) + line + char(close)
53
+ }
47
54
48
- def flatten (c : Conf ): Conf = c match {
49
- case Conf .Obj (obj) =>
50
- val flattened = obj.flatMap {
51
- case (key, Conf .Obj (nested)) =>
52
- nested.map {
53
- case (k, v) =>
54
- s " $key. $k" -> flatten(v)
55
- }
56
- case x => x :: Nil
57
- }
58
- Conf .Obj (flattened)
59
- case x => x
55
+ def toHocon [T : ConfEncoder ](value : T ): Doc = {
56
+ toHocon(ConfEncoder [T ].write(value))
57
+ }
58
+ def toHocon (conf : Conf ): Doc = {
59
+ def loop (c : Conf ): Doc = {
60
+ c match {
61
+ case Conf .Null () => text(" null" )
62
+ case Conf .Num (num) => str(num)
63
+ case Conf .Str (str) => quoteString(str)
64
+ case Conf .Bool (bool) => str(bool)
65
+ case Conf .Lst (lst) =>
66
+ if (lst.isEmpty) text(" []" )
67
+ else {
68
+ val parts = intercalate(line, lst.map {
69
+ case c : Conf .Obj =>
70
+ wrap('{' , '}' , loop(c))
71
+ case x => loop(x)
72
+ })
73
+ wrap('[' , ']' , parts)
74
+ }
75
+ case Conf .Obj (obj) =>
76
+ intercalate(line, obj.map {
77
+ case (k, v) =>
78
+ text(k) + text(" = " ) + loop(v)
79
+ })
80
+ // "[" +: (parts :+ " ]").nested(2)
81
+ // out.append("[\n")
82
+ // lst.foreach { elem =>
83
+ // indent(nesting + 2)
84
+ // elem match {
85
+ // case _: Conf.Obj =>
86
+ // out.append('{')
87
+ // newline()
88
+ // loop(elem, nesting + 4)
89
+ // indent(nesting + 2)
90
+ // out.append('}')
91
+ // case _ =>
92
+ // loop(elem, nesting + 2)
93
+ // }
94
+ // newline()
95
+ // }
96
+ // out.append("]")
97
+ // }
98
+ // case Conf.Obj(obj) =>
99
+ // if (obj.isEmpty) out.append("{}")
100
+ // else {
101
+ // obj.foreach {
102
+ // case (key, value) =>
103
+ // indent(nesting)
104
+ // out.append(key).append(" = ")
105
+ // loop(value, nesting + 2)
106
+ // newline()
107
+ // }
108
+ // }
109
+ }
60
110
}
111
+
61
112
loop(flatten(conf))
113
+ }
62
114
63
- out.toString()
115
+ def flatten (c : Conf ): Conf = c match {
116
+ case Conf .Obj (obj) =>
117
+ val flattened = obj.map {
118
+ case (k, v) => (k, flatten(v))
119
+ }
120
+ val next = flattened.flatMap {
121
+ case (key, Conf .Obj (nested)) =>
122
+ nested.map {
123
+ case (k, v) => s " ${quote(key)}. $k" -> v
124
+ }
125
+ case (key, value) => (quote(key), value) :: Nil
126
+ }
127
+ Conf .Obj (next)
128
+ case Conf .Lst (lst) =>
129
+ Conf .Lst (lst.map(flatten))
130
+ case x => x
64
131
}
65
132
66
133
def fromHoconString (
0 commit comments