mirror of
https://github.com/fluencelabs/aqua.git
synced 2025-06-23 19:41:33 +00:00
Removing IO from compiler submodule (#186)
* Removing IO from compiler submodule (wip) * move targets to cli
This commit is contained in:
@ -4,8 +4,7 @@ import aqua.backend.Backend
|
||||
import aqua.backend.air.AirBackend
|
||||
import aqua.backend.js.JavaScriptBackend
|
||||
import aqua.backend.ts.TypeScriptBackend
|
||||
import aqua.compiler.AquaCompiler
|
||||
import aqua.compiler.AquaCompiler.{AirTarget, CompileTarget, JavaScriptTarget, TypescriptTarget}
|
||||
import aqua.compiler.{AquaCompiler, AquaIO}
|
||||
import aqua.model.transform.BodyConfig
|
||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||
import cats.Id
|
||||
@ -34,6 +33,11 @@ object CustomLogFormatter extends LogFormatter {
|
||||
object AquaCli extends IOApp with LogSupport {
|
||||
import AppOps._
|
||||
|
||||
sealed trait CompileTarget
|
||||
case object TypescriptTarget extends CompileTarget
|
||||
case object JavaScriptTarget extends CompileTarget
|
||||
case object AirTarget extends CompileTarget
|
||||
|
||||
def targetToBackend(target: CompileTarget): Backend = {
|
||||
target match {
|
||||
case TypescriptTarget =>
|
||||
@ -68,13 +72,15 @@ object AquaCli extends IOApp with LogSupport {
|
||||
WLogger.setDefaultLogLevel(LogLevel.toLogLevel(logLevel))
|
||||
WLogger.setDefaultFormatter(CustomLogFormatter)
|
||||
|
||||
implicit val aio: AquaIO[F] = new AquaFilesIO[F]
|
||||
|
||||
// if there is `--help` or `--version` flag - show help and version
|
||||
// otherwise continue program execution
|
||||
h.map(_ => helpAndExit) orElse v.map(_ => versionAndExit) getOrElse {
|
||||
val target =
|
||||
if (toAir) AquaCompiler.AirTarget
|
||||
else if (toJs) AquaCompiler.JavaScriptTarget
|
||||
else AquaCompiler.TypescriptTarget
|
||||
if (toAir) AirTarget
|
||||
else if (toJs) JavaScriptTarget
|
||||
else TypescriptTarget
|
||||
val bc = {
|
||||
val bc = BodyConfig(wrapWithXor = !noXor, constants = constants)
|
||||
bc.copy(relayVarName = bc.relayVarName.filterNot(_ => noRelay))
|
||||
|
141
cli/src/main/scala/aqua/AquaFilesIO.scala
Normal file
141
cli/src/main/scala/aqua/AquaFilesIO.scala
Normal file
@ -0,0 +1,141 @@
|
||||
package aqua
|
||||
|
||||
import aqua.compiler.AquaIO
|
||||
import aqua.compiler.io.{
|
||||
AquaFileError,
|
||||
EmptyFileError,
|
||||
FileNotFound,
|
||||
FileSystemError,
|
||||
FileWriteError
|
||||
}
|
||||
import aqua.parser.lift.FileSpan
|
||||
import cats.data.Validated.{Invalid, Valid}
|
||||
import cats.data.{Chain, EitherT, NonEmptyChain, Validated, ValidatedNec}
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.either._
|
||||
import cats.effect.kernel.Concurrent
|
||||
import fs2.io.file.Files
|
||||
import fs2.text
|
||||
import cats.syntax.applicative._
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.apply._
|
||||
|
||||
import java.nio.file.Path
|
||||
import scala.util.Try
|
||||
|
||||
class AquaFilesIO[F[_]: Files: Concurrent] extends AquaIO[F] {
|
||||
|
||||
override def readFile(file: Path): EitherT[F, AquaFileError, String] =
|
||||
EitherT(
|
||||
Files[F]
|
||||
.readAll(file, 4096)
|
||||
.fold(Vector.empty[Byte])((acc, b) => acc :+ b)
|
||||
// TODO fix for comment on last line in air
|
||||
// TODO should be fixed by parser
|
||||
.map(_.appendedAll("\n\r".getBytes))
|
||||
.flatMap(fs2.Stream.emits)
|
||||
.through(text.utf8Decode)
|
||||
.attempt
|
||||
.compile
|
||||
.last
|
||||
.map(
|
||||
_.fold((EmptyFileError(file): AquaFileError).asLeft[String])(_.left.map(FileSystemError))
|
||||
)
|
||||
)
|
||||
|
||||
private def findFirstF(
|
||||
in: List[Path],
|
||||
notFound: EitherT[F, AquaFileError, Path]
|
||||
): EitherT[F, AquaFileError, Path] =
|
||||
in.headOption.fold(notFound)(p =>
|
||||
EitherT(
|
||||
Concurrent[F].attempt(p.toFile.isFile.pure[F])
|
||||
)
|
||||
.leftMap[AquaFileError](FileSystemError)
|
||||
.recover({ case _ => false })
|
||||
.flatMap {
|
||||
case true =>
|
||||
EitherT(
|
||||
Concurrent[F].attempt(p.toAbsolutePath.normalize().pure[F])
|
||||
).leftMap[AquaFileError](FileSystemError)
|
||||
case false =>
|
||||
findFirstF(in.tail, notFound)
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Checks if a file exists in the list of possible paths
|
||||
*/
|
||||
def resolve(
|
||||
focus: FileSpan.Focus,
|
||||
src: Path,
|
||||
imports: List[Path]
|
||||
): EitherT[F, AquaFileError, Path] =
|
||||
findFirstF(
|
||||
imports
|
||||
.map(_.resolve(src)),
|
||||
EitherT.leftT(FileNotFound(focus, src, imports))
|
||||
)
|
||||
|
||||
override def listAqua(folder: Path): F[ValidatedNec[AquaFileError, Chain[Path]]] =
|
||||
Validated
|
||||
.fromTry(
|
||||
Try {
|
||||
val f = folder.toFile
|
||||
if (f.isDirectory) {
|
||||
f.listFiles().toList
|
||||
} else {
|
||||
f :: Nil
|
||||
}
|
||||
}
|
||||
)
|
||||
.leftMap[AquaFileError](FileSystemError)
|
||||
.leftMap(NonEmptyChain.one)
|
||||
.pure[F]
|
||||
.flatMap {
|
||||
case Valid(files) =>
|
||||
files.collect {
|
||||
case f if f.isFile && f.getName.endsWith(".aqua") =>
|
||||
Validated
|
||||
.fromTry(
|
||||
Try(Chain.one(f.toPath.toAbsolutePath.normalize()))
|
||||
)
|
||||
.leftMap(FileSystemError)
|
||||
.leftMap(NonEmptyChain.one)
|
||||
.pure[F]
|
||||
case f if f.isDirectory =>
|
||||
listAqua(f.toPath)
|
||||
}.foldLeft(Validated.validNec[AquaFileError, Chain[Path]](Chain.nil).pure[F]) {
|
||||
case (acc, v) =>
|
||||
(acc, v).mapN(_ combine _)
|
||||
}
|
||||
case Invalid(errs) =>
|
||||
Validated.invalid[NonEmptyChain[AquaFileError], Chain[Path]](errs).pure[F]
|
||||
}
|
||||
|
||||
override def writeFile(file: Path, content: String): EitherT[F, AquaFileError, Unit] =
|
||||
EitherT
|
||||
.right[AquaFileError](Files[F].deleteIfExists(file))
|
||||
.flatMap(_ =>
|
||||
EitherT[F, AquaFileError, Unit](
|
||||
fs2.Stream
|
||||
.emit(
|
||||
content
|
||||
)
|
||||
.through(text.utf8Encode)
|
||||
.through(Files[F].writeAll(file))
|
||||
.attempt
|
||||
.map { e =>
|
||||
e.left
|
||||
.map(t => FileWriteError(file, t))
|
||||
}
|
||||
.compile
|
||||
.drain
|
||||
.map(_ => Right(()))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
object AquaFilesIO {
|
||||
implicit def summon[F[_]: Files: Concurrent]: AquaIO[F] = new AquaFilesIO[F]
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package aqua
|
||||
|
||||
import aqua.backend.ts.TypeScriptBackend
|
||||
import aqua.compiler.AquaCompiler
|
||||
import aqua.compiler.{AquaCompiler, AquaIO}
|
||||
import aqua.model.transform.BodyConfig
|
||||
import cats.data.Validated
|
||||
import cats.effect.{IO, IOApp, Sync}
|
||||
@ -15,6 +15,8 @@ object Test extends IOApp.Simple {
|
||||
implicit def logger[F[_]: Sync]: SelfAwareStructuredLogger[F] =
|
||||
Slf4jLogger.getLogger[F]
|
||||
|
||||
implicit val aio: AquaIO[IO] = new AquaFilesIO[IO]
|
||||
|
||||
override def run: IO[Unit] =
|
||||
AquaCompiler
|
||||
.compileFilesTo[IO](
|
||||
|
@ -17,6 +17,8 @@ class WriteFileSpec extends AnyFlatSpec with Matchers {
|
||||
val targetJs = Files.createTempDirectory("js")
|
||||
val targetAir = Files.createTempDirectory("air")
|
||||
|
||||
import aqua.AquaFilesIO.summon
|
||||
|
||||
val bc = BodyConfig()
|
||||
AquaCompiler
|
||||
.compileFilesTo[IO](src, List.empty, targetTs, TypeScriptBackend, bc)
|
||||
|
Reference in New Issue
Block a user