11package org .polystat .cli
22
33import cats .data .NonEmptyList
4+ import cats .data .ValidatedNel
45import cats .effect .ExitCode
56import cats .effect .IO
67import cats .effect .IOApp
@@ -32,18 +33,56 @@ object Main extends IOApp:
3233 )
3334 yield exitCode
3435
35- def warnMissingKeys (
36+ def findMissingKeys (
3637 givenKeys : List [String ],
3738 availableKeys : List [String ],
39+ ): ValidatedNel [ValidationError , Unit ] =
40+ val missingKeys = givenKeys.filterNot(rule => availableKeys.contains(rule))
41+ missingKeys.toNel match
42+ case Some (keys) => ValidationError .MissingAnalyzersKeys (keys).invalidNel
43+ case None => ().validNel
44+
45+ def validateInex (
46+ inex : Option [IncludeExclude ]
47+ ): ValidatedNel [ValidationError , NonEmptyList [EOAnalyzer ]] =
48+ val filtered = filterAnalyzers(inex).toNel
49+ val parsedKeys =
50+ inex
51+ .map {
52+ case Include (list) => list.toList
53+ case Exclude (list) => list.toList
54+ }
55+ .getOrElse(List ())
56+ (
57+ findMissingKeys(parsedKeys, EOAnalyzer .analyzers.map(_.ruleId)),
58+ filtered match
59+ case Some (filtered) => filtered.valid
60+ case None => ValidationError .NoAnalyzers (inex.get).invalidNel,
61+ ).mapN { case (_, filtered) =>
62+ filtered
63+ }
64+
65+ def reportValidationErrors (
66+ errors : NonEmptyList [ValidationError ]
3867 ): IO [Unit ] =
39- givenKeys.traverse_(rule =>
40- if availableKeys.contains(rule) then IO .unit
41- else
42- IO .println(
43- s " WARNING: The analyzer with the key ' $rule' does not exist. " +
44- s " Run 'polystat list' to get the list of the available keys. "
45- )
46- )
68+ errors
69+ .traverse_ {
70+ case ValidationError .NoAnalyzers (inex) =>
71+ val message = inex match
72+ case Include (include) =>
73+ s " WARNING: The 'includeRules' key with values \" ${include.mkString_(" , " )}\" excludes all the analyzers, so none were run! "
74+ case Exclude (exclude) =>
75+ s " WARNING: The 'excludeRules' key with values \" ${exclude.mkString_(" , " )}\" excludes all the analyzers, so none were run! "
76+ IO .println(message)
77+
78+ case ValidationError .MissingAnalyzersKeys (missing) =>
79+ missing.traverse_(rule =>
80+ IO .println(
81+ s " WARNING: The analyzer with the key ' $rule' does not exist. " +
82+ s " Run 'polystat list' to get the list of the available keys. "
83+ )
84+ )
85+ }
4786
4887 def filterAnalyzers (
4988 inex : Option [IncludeExclude ]
@@ -74,6 +113,7 @@ object Main extends IOApp:
74113 configFile <- config match
75114 case Some (file) => IO .pure(file)
76115 case None => File .fromPathFailFast(Path (" .polystat.conf" ))
116+ _ <- IO .println(s " Reading configuration from $configFile... " )
77117 config <- readConfigFromFile(configFile)
78118 result <- execute(config)
79119 yield result
@@ -85,66 +125,67 @@ object Main extends IOApp:
85125 case SupportedLanguage .Java (_, _) => " .java"
86126 case SupportedLanguage .Python => " .py"
87127 case SupportedLanguage .EO => " .eo"
88- for
89- tempDir <- tmp match
90- case Some (path) =>
91- IO .println(s " Cleaning ${path.absolute}... " ) *>
92- path.createDirIfDoesntExist.flatMap(_.clean)
93- case None =>
94- Files [IO ].createTempDirectory.flatMap(Directory .fromPathFailFast)
95- parsedKeys = inex
96- .map {
97- case Include (list) => list.toList
98- case Exclude (list) => list.toList
99- }
100- .getOrElse(List ())
101128
102- _ <- warnMissingKeys(parsedKeys, EOAnalyzer .analyzers.map(_.ruleId))
103- input <- input match
104- case Input .FromDirectory (dir) => dir.pure[IO ]
105- case Input .FromFile (file) =>
106- for
107- singleFileTmpDir <-
108- (tempDir / " singleFile" ).unsafeToDirectory.createDirIfDoesntExist
109- singleFileTmpPath =
110- (singleFileTmpDir / (file.filenameNoExt + ext)).unsafeToFile
111- _ <- singleFileTmpPath.unsafeToFile.createFileIfDoesntExist
112- _ <- readCodeFromFile(ext, file)
113- .map(_._2)
114- .through(fs2.text.utf8.encode)
115- .through(Files [IO ].writeAll(singleFileTmpPath))
116- .compile
117- .drain
118- yield singleFileTmpDir
119- case Input .FromStdin =>
120- for
121- stdinTmpDir <-
122- (tempDir / " stdin" ).unsafeToDirectory.createDirIfDoesntExist
123- stdinTmpFilePath =
124- (stdinTmpDir / (" stdin" + ext)).unsafeToFile
125- _ <- stdinTmpFilePath.createFileIfDoesntExist
126- _ <-
127- readCodeFromStdin
129+ def analyze (filtered : NonEmptyList [EOAnalyzer ]): IO [Unit ] =
130+ for
131+ tempDir <- tmp match
132+ case Some (path) =>
133+ IO .println(s " Cleaning ${path.absolute}... " ) *>
134+ path.createDirIfDoesntExist.flatMap(_.clean)
135+ case None =>
136+ Files [IO ].createTempDirectory.flatMap(
137+ Directory .fromPathFailFast
138+ )
139+ input <- input match
140+ case Input .FromDirectory (dir) => dir.pure[IO ]
141+ case Input .FromFile (file) =>
142+ for
143+ singleFileTmpDir <-
144+ (tempDir / " singleFile" ).unsafeToDirectory.createDirIfDoesntExist
145+ singleFileTmpPath =
146+ (singleFileTmpDir / (file.filenameNoExt + ext)).unsafeToFile
147+ _ <- singleFileTmpPath.unsafeToFile.createFileIfDoesntExist
148+ _ <- readCodeFromFile(ext, file)
149+ .map(_._2)
128150 .through(fs2.text.utf8.encode)
129- .through(Files [IO ].writeAll(stdinTmpFilePath ))
151+ .through(Files [IO ].writeAll(singleFileTmpPath ))
130152 .compile
131153 .drain
132- yield stdinTmpDir
154+ yield singleFileTmpDir
155+ case Input .FromStdin =>
156+ for
157+ stdinTmpDir <-
158+ (tempDir / " stdin" ).unsafeToDirectory.createDirIfDoesntExist
159+ stdinTmpFilePath =
160+ (stdinTmpDir / (" stdin" + ext)).unsafeToFile
161+ _ <- stdinTmpFilePath.createFileIfDoesntExist
162+ _ <-
163+ readCodeFromStdin
164+ .through(fs2.text.utf8.encode)
165+ .through(Files [IO ].writeAll(stdinTmpFilePath))
166+ .compile
167+ .drain
168+ yield stdinTmpDir
169+ processedConfig =
170+ ProcessedConfig (
171+ filteredAnalyzers = filtered,
172+ tempDir = tempDir,
173+ output = out,
174+ input = input,
175+ fmts = fmts,
176+ )
177+ _ <-
178+ lang match
179+ case SupportedLanguage .EO => EO .analyze(processedConfig)
180+ case SupportedLanguage .Java (j2eo, j2eoVersion) =>
181+ Java .analyze(j2eoVersion, j2eo, processedConfig)
182+ case SupportedLanguage .Python => Python .analyze(processedConfig)
183+ yield ()
133184
134- processedConfig = ProcessedConfig (
135- filteredAnalyzers = filterAnalyzers(inex),
136- tempDir = tempDir,
137- output = out,
138- input = input,
139- fmts = fmts,
140- )
141- analysisResults <-
142- lang match
143- case SupportedLanguage .EO => EO .analyze(processedConfig)
144- case SupportedLanguage .Java (j2eo, j2eoVersion) =>
145- Java .analyze(j2eoVersion, j2eo, processedConfig)
146- case SupportedLanguage .Python => Python .analyze(processedConfig)
147- yield ()
185+ validateInex(inex).fold(
186+ errors => reportValidationErrors(errors),
187+ filtered => analyze(filtered),
188+ )
148189 end execute
149190
150191end Main
0 commit comments