Skip to content

Commit 00a8a18

Browse files
committed
Merge branch 'type_safe_files'
2 parents 2e350f1 + e586686 commit 00a8a18

File tree

12 files changed

+230
-134
lines changed

12 files changed

+230
-134
lines changed

src/main/scala/org/polystat/cli/EO.scala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ import org.polystat.odin.analysis.EOOdinAnalyzer.OdinAnalysisResult
1111
import org.polystat.odin.parser.EoParser.sourceCodeEoParser
1212
import org.polystat.sarif.AggregatedSarifOutput
1313
import org.polystat.sarif.SarifOutput
14+
import org.polystat.cli.util.FileTypes.*
1415

1516
import PolystatConfig.*
1617

1718
object EO:
1819

1920
def analyze(cfg: ProcessedConfig): IO[Unit] =
2021
def runAnalyzers(
21-
inputFiles: Vector[(Path, String)]
22-
): IO[Vector[(Path, List[OdinAnalysisResult])]] =
22+
inputFiles: Vector[(File, String)]
23+
): IO[Vector[(File, List[OdinAnalysisResult])]] =
2324
inputFiles
2425
.traverse { case (codePath, code) =>
2526
for
@@ -40,7 +41,7 @@ object EO:
4041
}
4142

4243
def writeToDirs(
43-
analyzed: Vector[(Path, List[OdinAnalysisResult])]
44+
analyzed: Vector[(File, List[OdinAnalysisResult])]
4445
): IO[Unit] =
4546
analyzed.traverse_ { case (codePath, results) =>
4647
for
@@ -54,7 +55,7 @@ object EO:
5455
val outPath =
5556
codePath
5657
.mount(
57-
to = out / "sarif",
58+
to = (out / "sarif").unsafeToDirectory,
5859
relativelyTo =
5960
cfg.input.asInstanceOf[Input.FromDirectory].path,
6061
)
@@ -68,7 +69,7 @@ object EO:
6869
yield ()
6970
}
7071

71-
def writeAggregate(analyzed: Vector[(Path, List[OdinAnalysisResult])]) =
72+
def writeAggregate(analyzed: Vector[(File, List[OdinAnalysisResult])]) =
7273
cfg.output.files.traverse_ { outputPath =>
7374
cfg.fmts.traverse_ { case OutputFormat.Sarif =>
7475
for

src/main/scala/org/polystat/cli/EOAnalyzer.scala

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import org.polystat.odin.analysis.EOOdinAnalyzer
1010
import org.polystat.odin.analysis.EOOdinAnalyzer.OdinAnalysisResult
1111
import org.polystat.odin.analysis.liskov.Analyzer
1212
import org.polystat.odin.parser.EoParser.sourceCodeEoParser
13+
import org.polystat.cli.util.FileTypes.*
1314

1415
trait EOAnalyzer:
1516
def ruleId: String
1617
def analyze(
17-
tmpDir: Path,
18-
pathToSrcRoot: Path,
19-
pathToCode: Path,
18+
tmpDir: Directory,
19+
pathToSrcRoot: Directory,
20+
pathToCode: File,
2021
code: String,
2122
): IO[OdinAnalysisResult]
2223

@@ -42,12 +43,11 @@ object EOAnalyzer:
4243

4344
def fromOdinAstAnalyzer(_ruleId: String)(a: ASTAnalyzer[IO]): EOAnalyzer =
4445
new EOAnalyzer:
45-
4646
def ruleId: String = _ruleId
4747
def analyze(
48-
tmpDir: Path,
49-
pathToSrcRoot: Path,
50-
pathToCode: Path,
48+
tmpDir: Directory,
49+
pathToSrcRoot: Directory,
50+
pathToCode: File,
5151
code: String,
5252
): IO[OdinAnalysisResult] =
5353
EOOdinAnalyzer
@@ -60,17 +60,18 @@ object EOAnalyzer:
6060
case ok: OdinAnalysisResult.Ok => ok.copy(ruleId = ruleId)
6161
}
6262

63-
def farEOAnalyzer(_ruleId: String): EOAnalyzer = new EOAnalyzer:
64-
def ruleId: String = _ruleId
65-
def analyze(
66-
tmpDir: Path,
67-
pathToSrcRoot: Path,
68-
pathToCode: Path,
69-
code: String,
70-
): IO[OdinAnalysisResult] =
71-
Far.analyze(
72-
ruleId = ruleId,
73-
pathToSrcRoot = pathToSrcRoot,
74-
pathToTmpDir = tmpDir,
75-
pathToCode = pathToCode,
76-
)
63+
def farEOAnalyzer(_ruleId: String): EOAnalyzer =
64+
new EOAnalyzer:
65+
def ruleId: String = _ruleId
66+
def analyze(
67+
tmpDir: Directory,
68+
pathToSrcRoot: Directory,
69+
pathToCode: File,
70+
code: String,
71+
): IO[OdinAnalysisResult] =
72+
Far.analyze(
73+
ruleId = ruleId,
74+
pathToSrcRoot = pathToSrcRoot,
75+
pathToTmpDir = tmpDir,
76+
pathToCode = pathToCode,
77+
)

src/main/scala/org/polystat/cli/Far.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import org.polystat.odin.core.ast.EOProg
2121

2222
import java.nio.file.Path as JPath
2323
import scala.jdk.CollectionConverters.*
24+
import org.polystat.cli.util.FileTypes.*
2425

2526
object Far:
2627

@@ -44,13 +45,13 @@ object Far:
4445

4546
def analyze(
4647
ruleId: String,
47-
pathToSrcRoot: Path,
48-
pathToTmpDir: Path,
49-
pathToCode: Path,
48+
pathToSrcRoot: Directory,
49+
pathToTmpDir: Directory,
50+
pathToCode: File,
5051
): IO[OdinAnalysisResult] =
5152
val codeFileNameNoExt: String = pathToCode.filenameNoExt
5253
val createPathToXml: IO[JPath] =
53-
(pathToTmpDir / "xmir").createDirIfDoesntExist.map(tmp =>
54+
(pathToTmpDir / "xmir").unsafeToDirectory.createDirIfDoesntExist.map(tmp =>
5455
pathToCode
5556
.mount(to = tmp, relativelyTo = pathToSrcRoot)
5657
.replaceExt(newExt = ".xml")

src/main/scala/org/polystat/cli/HoconConfig.scala

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import scala.jdk.CollectionConverters.*
2121
import PolystatConfig.*
2222
import SupportedLanguage.*
2323
import IncludeExclude.*
24+
import org.polystat.cli.util.FileTypes.*
25+
import ciris.ConfigKey
2426

2527
case class HoconConfig(path: Path):
2628

@@ -33,7 +35,7 @@ case class HoconConfig(path: Path):
3335
val loadConfig: IO[Config] =
3436
IO.blocking(ConfigFactory.parseFile(path.toNioPath.toFile))
3537

36-
val parseConfig: IO[ConfigValue[IO, PolystatUsage.Analyze]] =
38+
val parseConfig: IO[ConfigValue[IO, IO[PolystatUsage.Analyze]]] =
3739
loadConfig.map(parsed =>
3840
given Config = parsed
3941
val lang = hocon(keys.inputLanguage).as[SupportedLanguage]
@@ -50,10 +52,15 @@ case class HoconConfig(path: Path):
5052
val outputsConsole =
5153
hocon(keys.outputsConsole).as[Boolean].default(false)
5254

53-
val outputs = (outputsDirs, outputsFiles, outputsConsole).parMapN {
54-
case (dirs, files, console) =>
55-
Output(dirs = dirs, files = files, console = console)
56-
}
55+
val outputs: ConfigValue[IO, IO[Output]] =
56+
(outputsDirs, outputsFiles, outputsConsole).parMapN {
57+
case (dirs, files, console) =>
58+
for
59+
dirs <- dirs.traverse(Directory.fromPathFailFast)
60+
files <- files.traverse(File.fromPathFailFast)
61+
yield Output(dirs = dirs, files = files, console = console)
62+
63+
}
5764

5865
val j2eoVersion = hocon(keys.j2eoVersion).as[String].option
5966
val outputFormats =
@@ -77,7 +84,11 @@ case class HoconConfig(path: Path):
7784
outputFormats,
7885
lang,
7986
) =>
80-
PolystatUsage.Analyze(
87+
for
88+
j2eo <- j2eo.traverse(File.fromPathFailFast)
89+
tmp <- tmp.traverse(Directory.fromPathFailFast)
90+
outputs <- outputs
91+
yield PolystatUsage.Analyze(
8192
language = lang match
8293
case Java(_, _) => Java(j2eo, j2eoVersion)
8394
case other => other
@@ -94,7 +105,14 @@ case class HoconConfig(path: Path):
94105
)
95106
end parseConfig
96107

97-
ConfigValue.eval(parseConfig)
108+
for
109+
parsed <- ConfigValue.eval(parseConfig)
110+
validated <- ConfigValue.eval(
111+
parsed.map(validated =>
112+
ConfigValue.loaded(ConfigKey(keys.toplevel), validated)
113+
)
114+
)
115+
yield validated
98116
end config
99117

100118
end HoconConfig

src/main/scala/org/polystat/cli/Java.scala

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import org.polystat.cli.util.InputUtils.*
1717

1818
import sys.process.*
1919
import PolystatConfig.*
20+
import org.polystat.cli.util.FileTypes.*
2021

2122
object Java:
2223

@@ -63,58 +64,53 @@ object Java:
6364

6465
private def runJ2EO(
6566
j2eoVersion: Option[String],
66-
j2eo: Option[Path],
67-
inputDir: Path,
68-
outputDir: Path,
67+
j2eo: Option[File],
68+
input: Directory | File,
69+
outputDir: Directory,
6970
): IO[Unit] =
7071
given inferredJ2eoVersion: String =
7172
j2eoVersion.getOrElse(DEFAULT_J2EO_VERSION)
7273
val inferredJ2eoPath = j2eo.getOrElse(j2eoPath)
7374
val command =
74-
s"java -jar ${inferredJ2eoPath} -o $outputDir $inputDir"
75+
s"java -jar ${inferredJ2eoPath} -o $outputDir $input"
7576
for
7677
j2eo <- j2eo
7778
.map(IO.pure)
7879
.getOrElse(defaultJ2EO)
79-
_ <- Files[IO]
80-
.exists(j2eo)
81-
.ifM(
82-
ifTrue = for
83-
_ <- IO.println(s"""Running "$command"...""")
84-
_ <- IO.blocking(command.!).void
85-
yield (),
86-
ifFalse = IO.println(s"""J2EO executable "$j2eo" doesn't exist!"""),
87-
)
80+
_ <- (for
81+
_ <- IO.println(s"""Running "$command"...""")
82+
_ <- IO.blocking(command.!).void
83+
yield ())
8884
yield ()
8985
end for
9086
end runJ2EO
9187

9288
def analyze(
9389
j2eoVersion: Option[String],
94-
j2eo: Option[Path],
90+
j2eo: Option[File],
9591
cfg: ProcessedConfig,
9692
): IO[Unit] =
9793
for
98-
dirForEO <- (cfg.tempDir / "eo").createDirIfDoesntExist
94+
dirForEO <- (cfg.tempDir / "eo").unsafeToDirectory.createDirIfDoesntExist
9995
_ <- cfg.input match // writing EO files to tempDir
10096
case Input.FromStdin =>
10197
for
10298
code <- readCodeFromStdin.compile.string
10399
stdinTmp <- Files[IO].createTempDirectory.map(path =>
104-
path / "stdin.java"
100+
(path / "stdin").unsafeToDirectory
105101
)
106102
_ <- writeOutputTo(stdinTmp)(code)
107103
_ <- runJ2EO(
108104
j2eoVersion,
109105
j2eo,
110-
inputDir = stdinTmp,
106+
input = stdinTmp,
111107
outputDir = dirForEO,
112108
)
113109
yield ()
114110
case Input.FromFile(path) =>
115-
runJ2EO(j2eoVersion, j2eo, inputDir = path, outputDir = dirForEO)
111+
runJ2EO(j2eoVersion, j2eo, input = path, outputDir = dirForEO)
116112
case Input.FromDirectory(path) =>
117-
runJ2EO(j2eoVersion, j2eo, inputDir = path, outputDir = dirForEO)
113+
runJ2EO(j2eoVersion, j2eo, input = path, outputDir = dirForEO)
118114
// J2EO deletes the tmp directory when there are no files to analyze
119115
// This causes the subsequent call to EO.analyze to fail, because there is no temp directory.
120116
// The line below patches this issue by creating the temp directory if it was deleted by J2EO.

src/main/scala/org/polystat/cli/Main.scala

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ import org.polystat.odin.analysis.EOOdinAnalyzer.OdinAnalysisResult
1919
import org.polystat.odin.parser.EoParser.sourceCodeEoParser
2020
import org.polystat.py2eo.parser.PythonLexer
2121
import org.polystat.py2eo.transpiler.Transpile
22+
import org.polystat.cli.util.FileTypes.*
2223

2324
import PolystatConfig.*
2425
import IncludeExclude.*
26+
2527
object Main extends IOApp:
2628
override def run(args: List[String]): IO[ExitCode] =
2729
for exitCode <- CommandIOApp.run(
@@ -68,8 +70,13 @@ object Main extends IOApp:
6870
case PolystatUsage.Misc(version, config) =>
6971
if (version) then IO.println(BuildInfo.versionSummary)
7072
else
71-
readConfigFromFile(config.getOrElse(Path(".polystat.conf")))
72-
.flatMap(execute)
73+
for
74+
configFile <- config match
75+
case Some(file) => IO.pure(file)
76+
case None => File.fromPathFailFast(Path(".polystat.conf"))
77+
config <- readConfigFromFile(configFile)
78+
result <- execute(config)
79+
yield result
7380
case PolystatUsage.Analyze(
7481
lang,
7582
AnalyzerConfig(inex, input, tmp, fmts, out),
@@ -79,7 +86,8 @@ object Main extends IOApp:
7986
case Some(path) =>
8087
IO.println(s"Cleaning ${path.absolute}...") *>
8188
path.createDirIfDoesntExist.flatMap(_.clean)
82-
case None => Files[IO].createTempDirectory
89+
case None =>
90+
Files[IO].createTempDirectory.flatMap(Directory.fromPathFailFast)
8391
parsedKeys = inex
8492
.map {
8593
case Include(list) => list.toList

src/main/scala/org/polystat/cli/PolystatConfig.scala

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,35 @@ import fs2.Stream
88
import fs2.io.file.Path
99
import org.polystat.cli.EOAnalyzer
1010
import org.polystat.odin.analysis.ASTAnalyzer
11+
import org.polystat.cli.util.FileTypes.*
1112

1213
object PolystatConfig:
1314

1415
final case class AnalyzerConfig(
1516
inex: Option[IncludeExclude],
1617
input: Input,
17-
tmp: Option[Path],
18+
tmp: Option[Directory],
1819
outputFormats: List[OutputFormat],
1920
output: Output,
2021
)
2122

2223
final case class ProcessedConfig(
2324
filteredAnalyzers: List[EOAnalyzer],
24-
tempDir: Path,
25+
tempDir: Directory,
2526
input: Input,
2627
fmts: List[OutputFormat],
2728
output: Output,
2829
)
2930

3031
enum SupportedLanguage:
3132
case EO, Python
32-
case Java(j2eo: Option[Path], j2eoVersion: Option[String])
33+
case Java(j2eo: Option[File], j2eoVersion: Option[String])
3334
end SupportedLanguage
3435

3536
enum PolystatUsage:
3637
case Analyze(language: SupportedLanguage, config: AnalyzerConfig)
3738
case List(config: Boolean)
38-
case Misc(version: Boolean, configPath: Option[Path])
39+
case Misc(version: Boolean, configPath: Option[File])
3940
end PolystatUsage
4041

4142
enum IncludeExclude:
@@ -44,17 +45,17 @@ object PolystatConfig:
4445
end IncludeExclude
4546

4647
enum Input:
47-
case FromDirectory(path: Path)
48-
case FromFile(path: Path)
48+
case FromDirectory(path: Directory)
49+
case FromFile(path: File)
4950
case FromStdin
5051
end Input
5152

5253
enum OutputFormat:
5354
case Sarif
5455

5556
final case class Output(
56-
dirs: List[Path],
57-
files: List[Path],
57+
dirs: List[Directory],
58+
files: List[File],
5859
console: Boolean,
5960
)
6061

0 commit comments

Comments
 (0)