diff --git a/chester-in-scala/common/js/src/main/scala/chester/utils/io/NodeHandler.scala b/chester-in-scala/common/js/src/main/scala/chester/utils/io/NodeHandler.scala index bb12c490..be4e3d0c 100644 --- a/chester-in-scala/common/js/src/main/scala/chester/utils/io/NodeHandler.scala +++ b/chester-in-scala/common/js/src/main/scala/chester/utils/io/NodeHandler.scala @@ -12,6 +12,7 @@ import scala.scalajs.js.typedarray.{Int8Array, Uint8Array} case class NodeHandlerSync[R]() extends Handler[R] with FileOpsEff { type P = String + def errorFilter(e: Throwable) = e.isInstanceOf[JavaScriptException] def pathOps = PathOpsString @@ -22,8 +23,8 @@ case class NodeHandlerSync[R]() extends Handler[R] with FileOpsEff { } def write(path: P, content: Array[Byte], append: Boolean) = use { k => - val result = Uint8Array.from(js.Array(content.map(x=>x.toShort)*)) - if(append) { + val result = Uint8Array.from(js.Array(content.map(x => x.toShort) *)) + if (append) { fsMod.appendFileSync(path, result) } else { fsMod.writeFileSync(path, result) @@ -31,6 +32,15 @@ case class NodeHandlerSync[R]() extends Handler[R] with FileOpsEff { k(()) } + override def writeString(path: P, content: String, append: Boolean) = use { k => + if (append) { + fsMod.appendFileSync(path, content) + } else { + fsMod.writeFileSync(path, content) + } + k(()) + } + def removeWhenExists(path: P) = use { k => if (fsMod.existsSync(path)) { fsMod.unlinkSync(path) @@ -47,12 +57,15 @@ case class NodeHandlerSync[R]() extends Handler[R] with FileOpsEff { def exists(path: P) = use { k => k(fsMod.existsSync(path)) } + def createDirIfNotExists(path: P) = use { k => if (!fsMod.existsSync(path)) { fsMod.mkdirSync(path) } k(()) } + + def downloadToFile(url: String, path: P) = ??? } inline def nodeHandlerSync[R](inline prog: FileOpsEff ?=> Control[R]): Control[R] = NodeHandlerSync[R]().handle(x => prog(using x)) \ No newline at end of file diff --git a/chester-in-scala/common/jvm-native/src/main/scala/chester/utils/io/NioHandler.scala b/chester-in-scala/common/jvm-native/src/main/scala/chester/utils/io/NioHandler.scala index 638a3b3f..9453600e 100644 --- a/chester-in-scala/common/jvm-native/src/main/scala/chester/utils/io/NioHandler.scala +++ b/chester-in-scala/common/jvm-native/src/main/scala/chester/utils/io/NioHandler.scala @@ -6,6 +6,42 @@ import effekt.{Control, Handler} import java.io.{BufferedReader, BufferedWriter, FileReader, FileWriter, IOException} import java.nio.charset.StandardCharsets import java.nio.file.{Files, Paths, StandardOpenOption} +import java.net.URL +import java.nio.file.{Files, Path, StandardCopyOption} +import java.io.IOException + +private object FileDownloader { + + @throws[IOException] + def downloadFile(urlString: String, targetPath: Path): Unit = { + val tempFile = Files.createTempFile(targetPath.getParent, "temp-", ".tmp") + + try { + val url = new URL(urlString) + val inputStream = url.openStream() + + try { + // Download the file to a temporary location + Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING) + + // Move the temporary file to the target location + Files.move(tempFile, targetPath, StandardCopyOption.REPLACE_EXISTING) + } finally { + inputStream.close() + } + + } catch { + case e: IOException => + // Clean up in case of failure + try { + Files.deleteIfExists(tempFile) + } catch { + case _: IOException => + } + throw e // Rethrow the exception to indicate failure + } + } +} trait NioOps extends FileOpsEff { type P = java.nio.file.Path @@ -67,6 +103,10 @@ case class NioHandler[R]() extends Handler[R] with NioOps { k(()) } + def downloadToFile(url: String, path: P) = use { k => + FileDownloader.downloadFile(url, path) + k(()) + } } inline def nioHandler[R](inline prog: FileOpsEff ?=> Control[R]): Control[R] = NioHandler[R]().handle(x => prog(using x)) diff --git a/chester-in-scala/common/shared/src/main/scala/chester/utils/io/Path.scala b/chester-in-scala/common/shared/src/main/scala/chester/utils/io/Path.scala index e3e024ae..d0abea85 100644 --- a/chester-in-scala/common/shared/src/main/scala/chester/utils/io/Path.scala +++ b/chester-in-scala/common/shared/src/main/scala/chester/utils/io/Path.scala @@ -60,6 +60,8 @@ trait FileOps { def exists(path: P): M[Boolean] def createDirIfNotExists(path: P): M[Unit] + + def downloadToFile(url: String, path: P): M[Unit] } object Files @@ -109,6 +111,8 @@ trait FileOpsFree extends FileOps { case class Read(path: P) extends Op[String] + case class WriteString(path: P, content: String, append: Boolean) extends Op[Unit] + case class Write(path: P, content: Array[Byte], append: Boolean) extends Op[Unit] case class RemoveWhenExists(path: P) extends Op[Boolean] @@ -120,6 +124,8 @@ trait FileOpsFree extends FileOps { case class CreateDirIfNotExists(path: P) extends Op[Unit] def read(path: P): M[String] = liftF(Read(path)) + + override def writeString(path: P, content: String, append: Boolean): M[Unit] = liftF(WriteString(path, content, append)) def write(path: P, content: Array[Byte], append: Boolean): M[Unit] = liftF(Write(path, content, append))