11package ru .d10xa .jsonlogviewer .decline .yaml
22
3- import cats .data .NonEmptyList
4- import cats .data .Validated
53import cats .data .ValidatedNel
64import cats .syntax .all .*
75import io .circe .*
6+ import io .circe .generic .semiauto .*
87import io .circe .yaml .scalayaml .parser
98import ru .d10xa .jsonlogviewer .decline .Config .FormatIn
109import ru .d10xa .jsonlogviewer .decline .FormatInValidator
@@ -21,239 +20,45 @@ class ConfigYamlLoaderImpl extends ConfigYamlLoader {
2120 .replace(" \\ n" , " " )
2221 .trim
2322
24- private def parseOptionalQueryAST (
25- fields : Map [String , Json ],
26- fieldName : String
27- ): ValidatedNel [String , Option [QueryAST ]] =
28- parseOptionalStringField(
29- fields,
30- fieldName,
31- s " Invalid ' $fieldName' field format "
32- ).andThen {
33- case Some (str) =>
34- val trimmed = trimCommentedLines(str)
35- QueryASTValidator .toValidatedQueryAST(trimmed).map(Some (_))
36- case None => Validated .valid(None )
37- }
38-
39- private def parseOptionalFormatIn (
40- fields : Map [String , Json ],
41- fieldName : String
42- ): ValidatedNel [String , Option [FormatIn ]] =
43- parseOptionalStringField(
44- fields,
45- fieldName,
46- s " Invalid ' $fieldName' field format "
47- ).andThen {
48- case Some (formatStr) =>
49- FormatInValidator .toValidatedFormatIn(formatStr).map(Some (_))
50- case None => Validated .valid(None )
51- }
52-
53- private def parseOptionalListString (
54- fields : Map [String , Json ],
55- fieldName : String
56- ): ValidatedNel [String , Option [List [String ]]] =
57- fields.get(fieldName) match {
58- case Some (jsonValue) =>
59- jsonValue
60- .as[List [String ]]
61- .leftMap(_ => s " Invalid ' $fieldName' field format " )
62- .toValidatedNel
63- .map(Some (_))
64- case None => Validated .valid(None )
65- }
66-
67- private def parseOptionalFeeds (
68- fields : Map [String , Json ],
69- fieldName : String
70- ): ValidatedNel [String , Option [List [Feed ]]] =
71- fields.get(fieldName) match {
72- case Some (jsonValue) =>
73- jsonValue
74- .as[List [Json ]]
75- .leftMap(_ => s " Invalid ' $fieldName' field format, should be a list " )
76- .toValidatedNel
77- .andThen(_.traverse(parseFeed))
78- .map(Some (_))
79- case None => Validated .valid(None )
80- }
81-
82- private def parseOptionalStringField (
83- fields : Map [String , Json ],
84- fieldName : String ,
85- errorMsg : String
86- ): ValidatedNel [String , Option [String ]] =
87- fields.get(fieldName) match {
88- case Some (jsonValue) =>
89- jsonValue.as[String ].leftMap(_ => errorMsg).toValidatedNel.map(Some (_))
90- case None => Validated .valid(None )
91- }
92-
93- private def parseString (
94- fields : Map [String , Json ],
95- fieldName : String ,
96- errorMsg : String
97- ): ValidatedNel [String , String ] =
98- fields.get(fieldName) match {
99- case Some (j) =>
100- j.as[String ].leftMap(_ => errorMsg).toValidatedNel
101- case None =>
102- Validated .invalidNel(s " Missing ' $fieldName' field in feed " )
103- }
104-
105- private def parseListString (
106- fields : Map [String , Json ],
107- fieldName : String
108- ): ValidatedNel [String , List [String ]] =
109- fields.get(fieldName) match {
110- case Some (c) =>
111- c.as[List [String ]]
112- .leftMap(_ => s " Invalid ' $fieldName' field in feed " )
113- .toValidatedNel
114- case None =>
115- Validated .invalidNel(s " Missing ' $fieldName' field in feed " )
116- }
117-
118- private def parseOptionalString (
119- fields : Map [String , Json ],
120- fieldName : String
121- ): ValidatedNel [String , Option [String ]] =
122- fields.get(fieldName) match {
123- case Some (c) =>
124- c.as[Option [String ]]
125- .leftMap(_ => s " Invalid ' $fieldName' field in feed " )
126- .toValidatedNel
127- case None =>
128- Validated .valid(None )
129- }
130-
131- private def parseOptionalFieldNames (
132- fields : Map [String , Json ],
133- fieldName : String
134- ): ValidatedNel [String , Option [FieldNames ]] =
135- fields.get(fieldName) match {
136- case Some (jsonValue) =>
137- jsonValue.asObject.map(_.toMap) match {
138- case None =>
139- Validated .invalidNel(
140- s " Invalid ' $fieldName' field format, should be an object "
141- )
142- case Some (fieldNamesFields) =>
143- val timestampValidated =
144- parseOptionalString(fieldNamesFields, " timestamp" )
145- val levelValidated = parseOptionalString(fieldNamesFields, " level" )
146- val messageValidated =
147- parseOptionalString(fieldNamesFields, " message" )
148- val stackTraceValidated =
149- parseOptionalString(fieldNamesFields, " stackTrace" )
150- val loggerNameValidated =
151- parseOptionalString(fieldNamesFields, " loggerName" )
152- val threadNameValidated =
153- parseOptionalString(fieldNamesFields, " threadName" )
154-
155- (
156- timestampValidated,
157- levelValidated,
158- messageValidated,
159- stackTraceValidated,
160- loggerNameValidated,
161- threadNameValidated
162- ).mapN(FieldNames .apply).map(Some (_))
163- }
164- case None => Validated .valid(None )
23+ private given Decoder [QueryAST ] = Decoder [String ].emap { str =>
24+ val trimmed = trimCommentedLines(str)
25+ QueryASTValidator .toValidatedQueryAST(trimmed).toEither.leftMap { errors =>
26+ errors.toList.mkString(" , " )
16527 }
28+ }
16629
167- private def parseOptionalBoolean (
168- fields : Map [String , Json ],
169- fieldName : String
170- ): ValidatedNel [String , Option [Boolean ]] =
171- fields.get(fieldName) match {
172- case Some (jsonValue) =>
173- jsonValue
174- .as[Boolean ]
175- .leftMap(_ =>
176- s " Invalid ' $fieldName' field format, should be a boolean "
177- )
178- .toValidatedNel
179- .map(Some (_))
180- case None => Validated .valid(None )
30+ private given Decoder [FormatIn ] = Decoder [String ].emap { formatStr =>
31+ FormatInValidator .toValidatedFormatIn(formatStr).toEither.leftMap { errors =>
32+ errors.toList.mkString(" , " )
18133 }
34+ }
18235
183- private def parseFeed (feedJson : Json ): ValidatedNel [String , Feed ] =
184- feedJson.asObject.map(_.toMap) match {
185- case None => Validated .invalidNel(" Feed entry is not a valid JSON object" )
186- case Some (feedFields) =>
187- val nameValidated = parseOptionalString(
188- feedFields,
189- " name"
190- )
191- val commandsValidated = parseListString(feedFields, " commands" )
192- val inlineInputValidated =
193- parseOptionalString(feedFields, " inlineInput" )
194- val filterValidated = parseOptionalQueryAST(feedFields, " filter" )
195- val formatInValidated
196- : Validated [NonEmptyList [String ], Option [FormatIn ]] =
197- parseOptionalFormatIn(feedFields, " formatIn" )
198- val fieldNamesValidated =
199- parseOptionalFieldNames(feedFields, " fieldNames" )
200- val rawIncludeValidated =
201- parseOptionalListString(feedFields, " rawInclude" )
202- val rawExcludeValidated =
203- parseOptionalListString(feedFields, " rawExclude" )
204- val fuzzyIncludeValidated =
205- parseOptionalListString(feedFields, " fuzzyInclude" )
206- val fuzzyExcludeValidated =
207- parseOptionalListString(feedFields, " fuzzyExclude" )
208- val excludeFieldsValidated =
209- parseOptionalListString(
210- feedFields,
211- " excludeFields"
212- )
213- val showEmptyFieldsValidated =
214- parseOptionalBoolean(feedFields, " showEmptyFields" )
215-
216- (
217- nameValidated,
218- commandsValidated,
219- inlineInputValidated,
220- filterValidated,
221- formatInValidated,
222- fieldNamesValidated,
223- rawIncludeValidated,
224- rawExcludeValidated,
225- fuzzyIncludeValidated,
226- fuzzyExcludeValidated,
227- excludeFieldsValidated,
228- showEmptyFieldsValidated
229- )
230- .mapN(Feed .apply)
231- }
36+ private given Decoder [FieldNames ] = deriveDecoder[FieldNames ]
37+ private given Decoder [Feed ] = deriveDecoder[Feed ]
38+ private given Decoder [ConfigYaml ] = deriveDecoder[ConfigYaml ]
23239
23340 def parseYamlFile (content : String ): ValidatedNel [String , ConfigYaml ] = {
23441 val uncommentedContent = content.linesIterator
23542 .filterNot(line => line.trim.startsWith(" #" ))
23643 .mkString(" \n " )
23744 .trim
45+
23846 if (uncommentedContent.isEmpty) {
239- Validated .valid(ConfigYaml .empty)
47+ cats.data. Validated .valid(ConfigYaml .empty)
24048 } else {
24149 parser.parse(content) match {
24250 case Left (error) =>
243- Validated .invalidNel(s " YAML parsing error: ${error.getMessage}" )
51+ cats.data.Validated .invalidNel(
52+ s " YAML parsing error: ${error.getMessage}"
53+ )
24454 case Right (json) =>
245- json.asObject.map(_.toMap) match {
246- case None => Validated .invalidNel(" YAML is not a valid JSON object" )
247- case Some (fields) =>
248- val feedsValidated : ValidatedNel [String , Option [List [Feed ]]] =
249- parseOptionalFeeds(fields, " feeds" )
250- val fieldNamesValidated =
251- parseOptionalFieldNames(fields, " fieldNames" )
252- val showEmptyFieldsValidated =
253- parseOptionalBoolean(fields, " showEmptyFields" )
254-
255- (fieldNamesValidated, feedsValidated, showEmptyFieldsValidated)
256- .mapN(ConfigYaml .apply)
55+ json.as[ConfigYaml ] match {
56+ case Right (config) =>
57+ cats.data.Validated .valid(config)
58+ case Left (error) =>
59+ cats.data.Validated .invalidNel(
60+ s " YAML validation error: ${error.getMessage}"
61+ )
25762 }
25863 }
25964 }
0 commit comments