1
1
package org .polystat .cli
2
2
3
+ import cats .data .NonEmptyList as Nel
3
4
import cats .effect .IO
4
5
import cats .syntax .all .*
5
6
import fs2 .io .file .Path
6
7
import io .circe .syntax .*
8
+ import org .polystat .cli .util .FileTypes .*
7
9
import org .polystat .cli .util .InputUtils .*
8
10
import org .polystat .odin .analysis .ASTAnalyzer
9
11
import org .polystat .odin .analysis .EOOdinAnalyzer
10
12
import org .polystat .odin .analysis .EOOdinAnalyzer .OdinAnalysisResult
11
13
import org .polystat .odin .parser .EoParser .sourceCodeEoParser
12
14
import org .polystat .sarif .AggregatedSarifOutput
13
15
import org .polystat .sarif .SarifOutput
14
- import org .polystat .cli .util .FileTypes .*
15
16
16
17
import PolystatConfig .*
17
18
18
19
object EO :
19
20
21
+ private def displayResult (
22
+ result : OdinAnalysisResult ,
23
+ indentDepth : Int ,
24
+ ): String =
25
+ def indent (d : Int ) = " \n " + " " * d
26
+ val indent1 = indent(indentDepth)
27
+ val indent2 = indent(indentDepth + 1 )
28
+ val messages : Nel [String ] = result match
29
+ case OdinAnalysisResult .Ok (ruleId) =>
30
+ Nel .one(s """ $indent1$ruleId: OK. """ )
31
+ case OdinAnalysisResult .DefectsDetected (ruleId, defects) =>
32
+ defects.map(msg => s """ $indent2$msg""" ).prepend(s " $indent1$ruleId: " )
33
+ case OdinAnalysisResult .AnalyzerFailure (ruleId, reason) =>
34
+ val reasonLines = reason.getMessage.trim
35
+ .split(scala.util.Properties .lineSeparator)
36
+ .map(" " * (indentDepth + 1 ) + " -- " + _)
37
+ .mkString(scala.util.Properties .lineSeparator)
38
+ Nel .one(
39
+ s """ $indent1$ruleId: Analyzer failed with the following reason:\n ${reasonLines}"""
40
+ )
41
+
42
+ messages.mkString_(" " )
43
+
44
+ private def displayAnalyzed (
45
+ analyzed : Vector [(File , List [OdinAnalysisResult ])]
46
+ ): IO [Unit ] =
47
+ analyzed.traverse_ { case (file, results) =>
48
+ IO .println(
49
+ s """ $file: ${results.map(res => displayResult(res, 1 )).mkString}
50
+ """ .stripMargin
51
+ )
52
+ }
53
+
20
54
def analyze (cfg : ProcessedConfig ): IO [Unit ] =
21
55
def runAnalyzers (
22
56
inputFiles : Vector [(File , String )]
@@ -43,9 +77,7 @@ object EO:
43
77
analyzed : Vector [(File , List [OdinAnalysisResult ])],
44
78
): IO [Unit ] =
45
79
analyzed.traverse_ { case (codePath, results) =>
46
- for
47
- _ <- if cfg.output.console then IO .println(analyzed) else IO .unit
48
- _ <- cfg.fmts.traverse_ { case OutputFormat .Sarif =>
80
+ for _ <- cfg.fmts.traverse_ { case OutputFormat .Sarif =>
49
81
val sarifJson = SarifOutput (
50
82
codePath,
51
83
results,
@@ -95,6 +127,7 @@ object EO:
95
127
outputFiles <- cfg.output.files.traverse(_.createFileIfDoesntExist)
96
128
_ <- writeToDirs(outputDirs, analyzed)
97
129
_ <- writeAggregate(outputFiles, analyzed)
130
+ _ <- if cfg.output.console then displayAnalyzed(analyzed) else IO .unit
98
131
yield ()
99
132
end for
100
133
end analyze
0 commit comments