Skip to content

Commit

Permalink
Merge pull request #237 from JD557/save-resource
Browse files Browse the repository at this point in the history
Add write operations to Resource
  • Loading branch information
JD557 authored Jul 23, 2022
2 parents 17a1cec + 7592b15 commit 5e6279a
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package eu.joaocosta.minart.backend

import java.io.{ByteArrayInputStream, InputStream}
import java.io.{ByteArrayInputStream, ByteArrayOutputStream, InputStream, OutputStream}

import scala.concurrent.{Future, Promise}
import scala.io.Source
import scala.scalajs.js
import scala.util.Try
import scala.util.{Try, Using}

import org.scalajs.dom
import org.scalajs.dom.{ProgressEvent, XMLHttpRequest}

import eu.joaocosta.minart.runtime.Resource
Expand All @@ -16,22 +17,35 @@ import eu.joaocosta.minart.runtime.Resource
final case class JsResource(resourcePath: String) extends Resource {
def path = "./" + resourcePath

private def loadFromLocalStorage(): Option[String] =
Option(dom.window.localStorage.getItem(resourcePath))

def withSource[A](f: Source => A): Try[A] = Try {
val xhr = new XMLHttpRequest()
xhr.open("GET", path, false)
xhr.send()
f(Source.fromString(xhr.responseText))
val data = loadFromLocalStorage() match {
case Some(d) => d
case None =>
val xhr = new XMLHttpRequest()
xhr.open("GET", path, false)
xhr.send()
xhr.responseText
}
f(Source.fromString(data))
}

def withSourceAsync[A](f: Source => A): Future[A] = {
val promise = Promise[A]()
val xhr = new XMLHttpRequest()
xhr.open("GET", path)
xhr.onloadend = (event: ProgressEvent) => {
if (xhr.status != 200) promise.failure(new Exception(xhr.statusText))
else promise.complete(Try(f(Source.fromString(xhr.responseText))))
loadFromLocalStorage() match {
case Some(data) =>
promise.complete(Try(f(Source.fromString(data))))
case None =>
val xhr = new XMLHttpRequest()
xhr.open("GET", path)
xhr.onloadend = (event: ProgressEvent) => {
if (xhr.status != 200) promise.failure(new Exception(xhr.statusText))
else promise.complete(Try(f(Source.fromString(xhr.responseText))))
}
xhr.send()
}
xhr.send()
promise.future
}

Expand All @@ -41,22 +55,39 @@ final case class JsResource(resourcePath: String) extends Resource {

def withInputStreamAsync[A](f: InputStream => A): Future[A] = {
val promise = Promise[A]()
val xhr = new XMLHttpRequest()
xhr.open("GET", path)
xhr.overrideMimeType("text/plain; charset=x-user-defined")
xhr.onloadend = (event: ProgressEvent) => {
if (xhr.status != 200) promise.failure(new Exception(xhr.statusText))
else promise.complete(Try(f(new ByteArrayInputStream(xhr.responseText.toCharArray.map(_.toByte)))))
loadFromLocalStorage() match {
case Some(data) =>
val is = new ByteArrayInputStream(data.toCharArray.map(_.toByte))
promise.complete(Try(f(is)))
case None =>
val xhr = new XMLHttpRequest()
xhr.open("GET", path)
xhr.overrideMimeType("text/plain; charset=x-user-defined")
xhr.onloadend = (event: ProgressEvent) => {
if (xhr.status != 200) promise.failure(new Exception(xhr.statusText))
else promise.complete(Try(f(new ByteArrayInputStream(xhr.responseText.toCharArray.map(_.toByte)))))
}
xhr.send()
}
xhr.send()
promise.future
}

def unsafeInputStream(): InputStream = {
val xhr = new XMLHttpRequest()
xhr.open("GET", path, false)
xhr.overrideMimeType("text/plain; charset=x-user-defined")
xhr.send()
new ByteArrayInputStream(xhr.responseText.toCharArray.map(_.toByte))
val data = loadFromLocalStorage() match {
case Some(d) => d
case None =>
val xhr = new XMLHttpRequest()
xhr.open("GET", path, false)
xhr.overrideMimeType("text/plain; charset=x-user-defined")
xhr.send()
xhr.responseText
}
new ByteArrayInputStream(data.toCharArray.map(_.toByte))
}

def withOutputStream(f: OutputStream => Unit): Unit =
Using[ByteArrayOutputStream, Unit](new ByteArrayOutputStream()) { os =>
f(os)
dom.window.localStorage.setItem(resourcePath, os.toByteArray().iterator.map(_.toChar).mkString(""))
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package eu.joaocosta.minart.backend

import java.io.{FileInputStream, InputStream}
import java.io.{FileInputStream, FileOutputStream, InputStream, OutputStream}

import scala.concurrent._
import scala.io.Source
Expand All @@ -23,10 +23,7 @@ final case class JavaResource(resourcePath: String) extends Resource {
def path = "./" + resourcePath
def withSource[A](f: Source => A): Try[A] = {
Using[Source, A](
Source.fromInputStream(
Try(Option(this.getClass().getResourceAsStream("/" + resourcePath)).get)
.getOrElse(new FileInputStream(path))
)
Source.fromInputStream(unsafeInputStream())
)(f)
}
def withSourceAsync[A](f: Source => A): Future[A] = Future(blocking(withSource(f)).get)
Expand All @@ -36,5 +33,8 @@ final case class JavaResource(resourcePath: String) extends Resource {

// TODO use Try(Source.fromResource(resourcePath)).getOrElse(Source.fromFile(path)) on scala 2.12+
def unsafeInputStream(): InputStream =
Try(Option(this.getClass().getResourceAsStream("/" + resourcePath)).get).getOrElse(new FileInputStream(path))
Try(new FileInputStream(path)).orElse(Try(Option(this.getClass().getResourceAsStream("/" + resourcePath)).get)).get

def withOutputStream(f: OutputStream => Unit): Unit =
Using[OutputStream, Unit](new FileOutputStream(path))(f)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package eu.joaocosta.minart.backend

import java.io.{FileInputStream, InputStream}
import java.io.{FileInputStream, FileOutputStream, InputStream, OutputStream}

import scala.concurrent.Future
import scala.io.Source
Expand All @@ -24,18 +24,19 @@ final case class NativeResource(resourcePath: String) extends Resource {
def path = "./" + resourcePath
def withSource[A](f: Source => A): Try[A] = {
Using[Source, A](
Source.fromInputStream(
Try(Option(this.getClass().getResourceAsStream("/" + resourcePath)).get)
.getOrElse(new FileInputStream(path))
)
Source.fromInputStream(unsafeInputStream())
)(f)
}
def withSourceAsync[A](f: Source => A): Future[A] =
Future.fromTry(withSource(f))
def withInputStream[A](f: InputStream => A): Try[A] = Using[InputStream, A](unsafeInputStream())(f)
def withInputStreamAsync[A](f: InputStream => A): Future[A] =
Future.fromTry(withInputStream(f))

// TODO use Try(Source.fromResource(resourcePath)).getOrElse(Source.fromFile(path)) on scala 2.12+
def unsafeInputStream(): InputStream =
Try(Option(this.getClass().getResourceAsStream("/" + resourcePath)).get).getOrElse(new FileInputStream(path))
Try(new FileInputStream(path)).orElse(Try(Option(this.getClass().getResourceAsStream("/" + resourcePath)).get)).get

def withOutputStream(f: OutputStream => Unit): Unit =
Using[OutputStream, Unit](new FileOutputStream(path))(f)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package eu.joaocosta.minart.runtime

import java.io.InputStream
import java.io.{InputStream, OutputStream}

import scala.concurrent.Future
import scala.io.Source
Expand Down Expand Up @@ -44,6 +44,11 @@ trait Resource {
* This method should only be used if for some reason the input stream must stay open (e.g. for data streaming)
*/
def unsafeInputStream(): InputStream

/** Provides a [[java.io.OutputStream]] to write data to this resource location.
* The OutputStream is closed in the end, so it should not escape this call.
*/
def withOutputStream(f: OutputStream => Unit): Unit
}

object Resource {
Expand Down

0 comments on commit 5e6279a

Please sign in to comment.