diff --git a/build.sbt b/build.sbt index 0aff685..98ec92b 100644 --- a/build.sbt +++ b/build.sbt @@ -42,6 +42,7 @@ excludeDependencies += // Gets transitively dragged in by zio-nio, conflicting with _3 ExclusionRule("org.scala-lang.modules", "scala-collection-compat_2.13") ) + .dependsOn(core) lazy val codecs = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) diff --git a/build.sbt b/build.sbt index 0aff685..98ec92b 100644 --- a/build.sbt +++ b/build.sbt @@ -42,6 +42,7 @@ excludeDependencies += // Gets transitively dragged in by zio-nio, conflicting with _3 ExclusionRule("org.scala-lang.modules", "scala-collection-compat_2.13") ) + .dependsOn(core) lazy val codecs = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) diff --git a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala index 0582cb5..16211a8 100644 --- a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala +++ b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala @@ -7,10 +7,12 @@ // TODO: improve error reporting on generic repositories // This is good just for prototypes -class JsStorageRepository[Value: JsonCodec](storage: Storage) - extends Repository[String, Value]: +// And it cannot be used to query data, as there is no way to iterate the storage +class JsStorageRepository[Value: JsonCodec]( + storage: Storage +) extends Repository[String, Value, String]: - override def find(id: String): UIO[Option[Value]] = { + override def load(id: String): UIO[Option[Value]] = { for raw <- ZIO.attemptBlocking(Option(storage.getItem(id))) data <- ZIO.foreach(raw) { r => @@ -23,3 +25,5 @@ override def save(key: String, value: Value): UIO[Unit] = ZIO.attemptBlocking(storage.setItem(key, value.toJson)).orDie + + override def find(id: String): UIO[List[Value]] = load(id).map(_.toList) diff --git a/build.sbt b/build.sbt index 0aff685..98ec92b 100644 --- a/build.sbt +++ b/build.sbt @@ -42,6 +42,7 @@ excludeDependencies += // Gets transitively dragged in by zio-nio, conflicting with _3 ExclusionRule("org.scala-lang.modules", "scala-collection-compat_2.13") ) + .dependsOn(core) lazy val codecs = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) diff --git a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala index 0582cb5..16211a8 100644 --- a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala +++ b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala @@ -7,10 +7,12 @@ // TODO: improve error reporting on generic repositories // This is good just for prototypes -class JsStorageRepository[Value: JsonCodec](storage: Storage) - extends Repository[String, Value]: +// And it cannot be used to query data, as there is no way to iterate the storage +class JsStorageRepository[Value: JsonCodec]( + storage: Storage +) extends Repository[String, Value, String]: - override def find(id: String): UIO[Option[Value]] = { + override def load(id: String): UIO[Option[Value]] = { for raw <- ZIO.attemptBlocking(Option(storage.getItem(id))) data <- ZIO.foreach(raw) { r => @@ -23,3 +25,5 @@ override def save(key: String, value: Value): UIO[Unit] = ZIO.attemptBlocking(storage.setItem(key, value.toJson)).orDie + + override def find(id: String): UIO[List[Value]] = load(id).map(_.toList) diff --git a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala index 273113e..43b1841 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala @@ -2,15 +2,14 @@ import works.iterative.core.FileSupport.* import works.iterative.core.FileRef -import works.iterative.core.UserMessage import zio.* trait FileStore: - type Op[A] = IO[UserMessage, A] + type Op[A] = UIO[A] def store(file: FileRepr): Op[FileRef] object FileStore: - type Op[A] = ZIO[FileStore, UserMessage, A] + type Op[A] = URIO[FileStore, A] def store(file: FileRepr): Op[FileRef] = ZIO.serviceWithZIO(_.store(file)) diff --git a/build.sbt b/build.sbt index 0aff685..98ec92b 100644 --- a/build.sbt +++ b/build.sbt @@ -42,6 +42,7 @@ excludeDependencies += // Gets transitively dragged in by zio-nio, conflicting with _3 ExclusionRule("org.scala-lang.modules", "scala-collection-compat_2.13") ) + .dependsOn(core) lazy val codecs = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) diff --git a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala index 0582cb5..16211a8 100644 --- a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala +++ b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala @@ -7,10 +7,12 @@ // TODO: improve error reporting on generic repositories // This is good just for prototypes -class JsStorageRepository[Value: JsonCodec](storage: Storage) - extends Repository[String, Value]: +// And it cannot be used to query data, as there is no way to iterate the storage +class JsStorageRepository[Value: JsonCodec]( + storage: Storage +) extends Repository[String, Value, String]: - override def find(id: String): UIO[Option[Value]] = { + override def load(id: String): UIO[Option[Value]] = { for raw <- ZIO.attemptBlocking(Option(storage.getItem(id))) data <- ZIO.foreach(raw) { r => @@ -23,3 +25,5 @@ override def save(key: String, value: Value): UIO[Unit] = ZIO.attemptBlocking(storage.setItem(key, value.toJson)).orDie + + override def find(id: String): UIO[List[Value]] = load(id).map(_.toList) diff --git a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala index 273113e..43b1841 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala @@ -2,15 +2,14 @@ import works.iterative.core.FileSupport.* import works.iterative.core.FileRef -import works.iterative.core.UserMessage import zio.* trait FileStore: - type Op[A] = IO[UserMessage, A] + type Op[A] = UIO[A] def store(file: FileRepr): Op[FileRef] object FileStore: - type Op[A] = ZIO[FileStore, UserMessage, A] + type Op[A] = URIO[FileStore, A] def store(file: FileRepr): Op[FileRef] = ZIO.serviceWithZIO(_.store(file)) diff --git a/core/shared/src/main/scala/works/iterative/core/service/Repository.scala b/core/shared/src/main/scala/works/iterative/core/service/Repository.scala index dc7d292..c945947 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/Repository.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/Repository.scala @@ -2,14 +2,30 @@ import zio.* -trait ReadRepository[-Key, +Value]: - type Op[A] = UIO[A] - def find(id: Key): Op[Option[Value]] +trait GenericReadRepository[Eff[+_], Coll[+_], -Key, +Value, -FilterArg]: + type Op[A] = Eff[A] -trait WriteRepository[-Key, -Value]: - type Op[A] = UIO[A] + def load(id: Key): Op[Option[Value]] + def loadAll(ids: Seq[Key]): Op[Coll[Value]] + def find(filter: FilterArg): Op[Coll[Value]] + +trait GenericWriteRepository[Eff[_], -Key, -Value]: + type Op[A] = Eff[A] def save(key: Key, value: Value): Op[Unit] -trait Repository[-Key, Value] - extends ReadRepository[Key, Value] +trait GenericRepository[Eff[+_], -Key, Value] + extends GenericReadRepository[Eff, List, Key, Value, Unit] + with GenericWriteRepository[Eff, Key, Value] + +trait ReadRepository[-Key, +Value, -FilterArg] + extends GenericReadRepository[UIO, List, Key, Value, FilterArg]: + override def loadAll(ids: Seq[Key]): UIO[List[Value]] = + // Inefficient implementation, meant to be overridden + ZIO.foreach(ids)(load).map(_.flatten.toList) + +trait WriteRepository[-Key, -Value] + extends GenericWriteRepository[UIO, Key, Value] + +trait Repository[-Key, Value, -FilterArg] + extends ReadRepository[Key, Value, FilterArg] with WriteRepository[Key, Value] diff --git a/build.sbt b/build.sbt index 0aff685..98ec92b 100644 --- a/build.sbt +++ b/build.sbt @@ -42,6 +42,7 @@ excludeDependencies += // Gets transitively dragged in by zio-nio, conflicting with _3 ExclusionRule("org.scala-lang.modules", "scala-collection-compat_2.13") ) + .dependsOn(core) lazy val codecs = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) diff --git a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala index 0582cb5..16211a8 100644 --- a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala +++ b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala @@ -7,10 +7,12 @@ // TODO: improve error reporting on generic repositories // This is good just for prototypes -class JsStorageRepository[Value: JsonCodec](storage: Storage) - extends Repository[String, Value]: +// And it cannot be used to query data, as there is no way to iterate the storage +class JsStorageRepository[Value: JsonCodec]( + storage: Storage +) extends Repository[String, Value, String]: - override def find(id: String): UIO[Option[Value]] = { + override def load(id: String): UIO[Option[Value]] = { for raw <- ZIO.attemptBlocking(Option(storage.getItem(id))) data <- ZIO.foreach(raw) { r => @@ -23,3 +25,5 @@ override def save(key: String, value: Value): UIO[Unit] = ZIO.attemptBlocking(storage.setItem(key, value.toJson)).orDie + + override def find(id: String): UIO[List[Value]] = load(id).map(_.toList) diff --git a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala index 273113e..43b1841 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala @@ -2,15 +2,14 @@ import works.iterative.core.FileSupport.* import works.iterative.core.FileRef -import works.iterative.core.UserMessage import zio.* trait FileStore: - type Op[A] = IO[UserMessage, A] + type Op[A] = UIO[A] def store(file: FileRepr): Op[FileRef] object FileStore: - type Op[A] = ZIO[FileStore, UserMessage, A] + type Op[A] = URIO[FileStore, A] def store(file: FileRepr): Op[FileRef] = ZIO.serviceWithZIO(_.store(file)) diff --git a/core/shared/src/main/scala/works/iterative/core/service/Repository.scala b/core/shared/src/main/scala/works/iterative/core/service/Repository.scala index dc7d292..c945947 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/Repository.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/Repository.scala @@ -2,14 +2,30 @@ import zio.* -trait ReadRepository[-Key, +Value]: - type Op[A] = UIO[A] - def find(id: Key): Op[Option[Value]] +trait GenericReadRepository[Eff[+_], Coll[+_], -Key, +Value, -FilterArg]: + type Op[A] = Eff[A] -trait WriteRepository[-Key, -Value]: - type Op[A] = UIO[A] + def load(id: Key): Op[Option[Value]] + def loadAll(ids: Seq[Key]): Op[Coll[Value]] + def find(filter: FilterArg): Op[Coll[Value]] + +trait GenericWriteRepository[Eff[_], -Key, -Value]: + type Op[A] = Eff[A] def save(key: Key, value: Value): Op[Unit] -trait Repository[-Key, Value] - extends ReadRepository[Key, Value] +trait GenericRepository[Eff[+_], -Key, Value] + extends GenericReadRepository[Eff, List, Key, Value, Unit] + with GenericWriteRepository[Eff, Key, Value] + +trait ReadRepository[-Key, +Value, -FilterArg] + extends GenericReadRepository[UIO, List, Key, Value, FilterArg]: + override def loadAll(ids: Seq[Key]): UIO[List[Value]] = + // Inefficient implementation, meant to be overridden + ZIO.foreach(ids)(load).map(_.flatten.toList) + +trait WriteRepository[-Key, -Value] + extends GenericWriteRepository[UIO, Key, Value] + +trait Repository[-Key, Value, -FilterArg] + extends ReadRepository[Key, Value, FilterArg] with WriteRepository[Key, Value] diff --git a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala index e2004d7..9420dbf 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala @@ -9,6 +9,5 @@ val layer: ULayer[FileStore] = ZLayer.succeed { new FileStore: override def store(file: FileRepr): Op[FileRef] = - for ref <- FileRef(file.name, "#").toZIO - yield ref + ZIO.succeed(FileRef.unsafe(file.name, "#")) } diff --git a/build.sbt b/build.sbt index 0aff685..98ec92b 100644 --- a/build.sbt +++ b/build.sbt @@ -42,6 +42,7 @@ excludeDependencies += // Gets transitively dragged in by zio-nio, conflicting with _3 ExclusionRule("org.scala-lang.modules", "scala-collection-compat_2.13") ) + .dependsOn(core) lazy val codecs = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) diff --git a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala index 0582cb5..16211a8 100644 --- a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala +++ b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala @@ -7,10 +7,12 @@ // TODO: improve error reporting on generic repositories // This is good just for prototypes -class JsStorageRepository[Value: JsonCodec](storage: Storage) - extends Repository[String, Value]: +// And it cannot be used to query data, as there is no way to iterate the storage +class JsStorageRepository[Value: JsonCodec]( + storage: Storage +) extends Repository[String, Value, String]: - override def find(id: String): UIO[Option[Value]] = { + override def load(id: String): UIO[Option[Value]] = { for raw <- ZIO.attemptBlocking(Option(storage.getItem(id))) data <- ZIO.foreach(raw) { r => @@ -23,3 +25,5 @@ override def save(key: String, value: Value): UIO[Unit] = ZIO.attemptBlocking(storage.setItem(key, value.toJson)).orDie + + override def find(id: String): UIO[List[Value]] = load(id).map(_.toList) diff --git a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala index 273113e..43b1841 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala @@ -2,15 +2,14 @@ import works.iterative.core.FileSupport.* import works.iterative.core.FileRef -import works.iterative.core.UserMessage import zio.* trait FileStore: - type Op[A] = IO[UserMessage, A] + type Op[A] = UIO[A] def store(file: FileRepr): Op[FileRef] object FileStore: - type Op[A] = ZIO[FileStore, UserMessage, A] + type Op[A] = URIO[FileStore, A] def store(file: FileRepr): Op[FileRef] = ZIO.serviceWithZIO(_.store(file)) diff --git a/core/shared/src/main/scala/works/iterative/core/service/Repository.scala b/core/shared/src/main/scala/works/iterative/core/service/Repository.scala index dc7d292..c945947 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/Repository.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/Repository.scala @@ -2,14 +2,30 @@ import zio.* -trait ReadRepository[-Key, +Value]: - type Op[A] = UIO[A] - def find(id: Key): Op[Option[Value]] +trait GenericReadRepository[Eff[+_], Coll[+_], -Key, +Value, -FilterArg]: + type Op[A] = Eff[A] -trait WriteRepository[-Key, -Value]: - type Op[A] = UIO[A] + def load(id: Key): Op[Option[Value]] + def loadAll(ids: Seq[Key]): Op[Coll[Value]] + def find(filter: FilterArg): Op[Coll[Value]] + +trait GenericWriteRepository[Eff[_], -Key, -Value]: + type Op[A] = Eff[A] def save(key: Key, value: Value): Op[Unit] -trait Repository[-Key, Value] - extends ReadRepository[Key, Value] +trait GenericRepository[Eff[+_], -Key, Value] + extends GenericReadRepository[Eff, List, Key, Value, Unit] + with GenericWriteRepository[Eff, Key, Value] + +trait ReadRepository[-Key, +Value, -FilterArg] + extends GenericReadRepository[UIO, List, Key, Value, FilterArg]: + override def loadAll(ids: Seq[Key]): UIO[List[Value]] = + // Inefficient implementation, meant to be overridden + ZIO.foreach(ids)(load).map(_.flatten.toList) + +trait WriteRepository[-Key, -Value] + extends GenericWriteRepository[UIO, Key, Value] + +trait Repository[-Key, Value, -FilterArg] + extends ReadRepository[Key, Value, FilterArg] with WriteRepository[Key, Value] diff --git a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala index e2004d7..9420dbf 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala @@ -9,6 +9,5 @@ val layer: ULayer[FileStore] = ZLayer.succeed { new FileStore: override def store(file: FileRepr): Op[FileRef] = - for ref <- FileRef(file.name, "#").toZIO - yield ref + ZIO.succeed(FileRef.unsafe(file.name, "#")) } diff --git a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala index 3c83b59..e3b7d45 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala @@ -3,9 +3,15 @@ import zio.* -trait InMemoryRepository[Key, Value](data: Ref[Map[Key, Value]]) - extends Repository[Key, Value]: +trait InMemoryRepository[Key, Value, FilterArg]( + data: Ref[Map[Key, Value]], + filter: FilterArg => Value => Boolean +) extends Repository[Key, Value, FilterArg]: override def save(key: Key, value: Value): UIO[Unit] = data.update(_ + (key -> value)) - override def find(key: Key): UIO[Option[Value]] = + override def load(key: Key): UIO[Option[Value]] = data.get.map(_.get(key)) + override def loadAll(keys: Seq[Key]): UIO[List[Value]] = + data.get.map(_.view.filterKeys(keys.contains).values.toList) + override def find(filterArg: FilterArg): UIO[List[Value]] = + data.get.map(_.values.filter(filter(filterArg)).toList) diff --git a/build.sbt b/build.sbt index 0aff685..98ec92b 100644 --- a/build.sbt +++ b/build.sbt @@ -42,6 +42,7 @@ excludeDependencies += // Gets transitively dragged in by zio-nio, conflicting with _3 ExclusionRule("org.scala-lang.modules", "scala-collection-compat_2.13") ) + .dependsOn(core) lazy val codecs = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) diff --git a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala index 0582cb5..16211a8 100644 --- a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala +++ b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala @@ -7,10 +7,12 @@ // TODO: improve error reporting on generic repositories // This is good just for prototypes -class JsStorageRepository[Value: JsonCodec](storage: Storage) - extends Repository[String, Value]: +// And it cannot be used to query data, as there is no way to iterate the storage +class JsStorageRepository[Value: JsonCodec]( + storage: Storage +) extends Repository[String, Value, String]: - override def find(id: String): UIO[Option[Value]] = { + override def load(id: String): UIO[Option[Value]] = { for raw <- ZIO.attemptBlocking(Option(storage.getItem(id))) data <- ZIO.foreach(raw) { r => @@ -23,3 +25,5 @@ override def save(key: String, value: Value): UIO[Unit] = ZIO.attemptBlocking(storage.setItem(key, value.toJson)).orDie + + override def find(id: String): UIO[List[Value]] = load(id).map(_.toList) diff --git a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala index 273113e..43b1841 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala @@ -2,15 +2,14 @@ import works.iterative.core.FileSupport.* import works.iterative.core.FileRef -import works.iterative.core.UserMessage import zio.* trait FileStore: - type Op[A] = IO[UserMessage, A] + type Op[A] = UIO[A] def store(file: FileRepr): Op[FileRef] object FileStore: - type Op[A] = ZIO[FileStore, UserMessage, A] + type Op[A] = URIO[FileStore, A] def store(file: FileRepr): Op[FileRef] = ZIO.serviceWithZIO(_.store(file)) diff --git a/core/shared/src/main/scala/works/iterative/core/service/Repository.scala b/core/shared/src/main/scala/works/iterative/core/service/Repository.scala index dc7d292..c945947 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/Repository.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/Repository.scala @@ -2,14 +2,30 @@ import zio.* -trait ReadRepository[-Key, +Value]: - type Op[A] = UIO[A] - def find(id: Key): Op[Option[Value]] +trait GenericReadRepository[Eff[+_], Coll[+_], -Key, +Value, -FilterArg]: + type Op[A] = Eff[A] -trait WriteRepository[-Key, -Value]: - type Op[A] = UIO[A] + def load(id: Key): Op[Option[Value]] + def loadAll(ids: Seq[Key]): Op[Coll[Value]] + def find(filter: FilterArg): Op[Coll[Value]] + +trait GenericWriteRepository[Eff[_], -Key, -Value]: + type Op[A] = Eff[A] def save(key: Key, value: Value): Op[Unit] -trait Repository[-Key, Value] - extends ReadRepository[Key, Value] +trait GenericRepository[Eff[+_], -Key, Value] + extends GenericReadRepository[Eff, List, Key, Value, Unit] + with GenericWriteRepository[Eff, Key, Value] + +trait ReadRepository[-Key, +Value, -FilterArg] + extends GenericReadRepository[UIO, List, Key, Value, FilterArg]: + override def loadAll(ids: Seq[Key]): UIO[List[Value]] = + // Inefficient implementation, meant to be overridden + ZIO.foreach(ids)(load).map(_.flatten.toList) + +trait WriteRepository[-Key, -Value] + extends GenericWriteRepository[UIO, Key, Value] + +trait Repository[-Key, Value, -FilterArg] + extends ReadRepository[Key, Value, FilterArg] with WriteRepository[Key, Value] diff --git a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala index e2004d7..9420dbf 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala @@ -9,6 +9,5 @@ val layer: ULayer[FileStore] = ZLayer.succeed { new FileStore: override def store(file: FileRepr): Op[FileRef] = - for ref <- FileRef(file.name, "#").toZIO - yield ref + ZIO.succeed(FileRef.unsafe(file.name, "#")) } diff --git a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala index 3c83b59..e3b7d45 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala @@ -3,9 +3,15 @@ import zio.* -trait InMemoryRepository[Key, Value](data: Ref[Map[Key, Value]]) - extends Repository[Key, Value]: +trait InMemoryRepository[Key, Value, FilterArg]( + data: Ref[Map[Key, Value]], + filter: FilterArg => Value => Boolean +) extends Repository[Key, Value, FilterArg]: override def save(key: Key, value: Value): UIO[Unit] = data.update(_ + (key -> value)) - override def find(key: Key): UIO[Option[Value]] = + override def load(key: Key): UIO[Option[Value]] = data.get.map(_.get(key)) + override def loadAll(keys: Seq[Key]): UIO[List[Value]] = + data.get.map(_.view.filterKeys(keys.contains).values.toList) + override def find(filterArg: FilterArg): UIO[List[Value]] = + data.get.map(_.values.filter(filter(filterArg)).toList) diff --git a/tapir/js/src/main/scala/works/iterative/tapir/CustomTapirPlatformSpecific.scala b/tapir/js/src/main/scala/works/iterative/tapir/CustomTapirPlatformSpecific.scala index 5421105..32b3902 100644 --- a/tapir/js/src/main/scala/works/iterative/tapir/CustomTapirPlatformSpecific.scala +++ b/tapir/js/src/main/scala/works/iterative/tapir/CustomTapirPlatformSpecific.scala @@ -1,6 +1,7 @@ package works.iterative.tapir import zio.* +import zio.stream.* import sttp.tapir.client.sttp.SttpClientInterpreter import sttp.tapir.PublicEndpoint import sttp.tapir.client.sttp.WebSocketToPipe @@ -11,6 +12,9 @@ import org.scalajs.dom import sttp.client3.impl.zio.FetchZioBackend import sttp.tapir.DecodeResult +import works.iterative.core.FileSupport +import works.iterative.core.FileSupport.* +import sttp.model.Part trait CustomTapirPlatformSpecific extends SttpClientInterpreter: self: CustomTapir => @@ -26,6 +30,12 @@ ) ) + extension (f: FileRepr) + def toPart: Task[Part[Array[Byte]]] = + f.toStream.run(ZSink.collectAll).map { bytes => + Part("file", bytes.toArray, fileName = Some(f.name)) + } + def makeClient[I, E, O]( endpoint: PublicEndpoint[I, E, O, Any] )(using diff --git a/build.sbt b/build.sbt index 0aff685..98ec92b 100644 --- a/build.sbt +++ b/build.sbt @@ -42,6 +42,7 @@ excludeDependencies += // Gets transitively dragged in by zio-nio, conflicting with _3 ExclusionRule("org.scala-lang.modules", "scala-collection-compat_2.13") ) + .dependsOn(core) lazy val codecs = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) diff --git a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala index 0582cb5..16211a8 100644 --- a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala +++ b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala @@ -7,10 +7,12 @@ // TODO: improve error reporting on generic repositories // This is good just for prototypes -class JsStorageRepository[Value: JsonCodec](storage: Storage) - extends Repository[String, Value]: +// And it cannot be used to query data, as there is no way to iterate the storage +class JsStorageRepository[Value: JsonCodec]( + storage: Storage +) extends Repository[String, Value, String]: - override def find(id: String): UIO[Option[Value]] = { + override def load(id: String): UIO[Option[Value]] = { for raw <- ZIO.attemptBlocking(Option(storage.getItem(id))) data <- ZIO.foreach(raw) { r => @@ -23,3 +25,5 @@ override def save(key: String, value: Value): UIO[Unit] = ZIO.attemptBlocking(storage.setItem(key, value.toJson)).orDie + + override def find(id: String): UIO[List[Value]] = load(id).map(_.toList) diff --git a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala index 273113e..43b1841 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala @@ -2,15 +2,14 @@ import works.iterative.core.FileSupport.* import works.iterative.core.FileRef -import works.iterative.core.UserMessage import zio.* trait FileStore: - type Op[A] = IO[UserMessage, A] + type Op[A] = UIO[A] def store(file: FileRepr): Op[FileRef] object FileStore: - type Op[A] = ZIO[FileStore, UserMessage, A] + type Op[A] = URIO[FileStore, A] def store(file: FileRepr): Op[FileRef] = ZIO.serviceWithZIO(_.store(file)) diff --git a/core/shared/src/main/scala/works/iterative/core/service/Repository.scala b/core/shared/src/main/scala/works/iterative/core/service/Repository.scala index dc7d292..c945947 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/Repository.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/Repository.scala @@ -2,14 +2,30 @@ import zio.* -trait ReadRepository[-Key, +Value]: - type Op[A] = UIO[A] - def find(id: Key): Op[Option[Value]] +trait GenericReadRepository[Eff[+_], Coll[+_], -Key, +Value, -FilterArg]: + type Op[A] = Eff[A] -trait WriteRepository[-Key, -Value]: - type Op[A] = UIO[A] + def load(id: Key): Op[Option[Value]] + def loadAll(ids: Seq[Key]): Op[Coll[Value]] + def find(filter: FilterArg): Op[Coll[Value]] + +trait GenericWriteRepository[Eff[_], -Key, -Value]: + type Op[A] = Eff[A] def save(key: Key, value: Value): Op[Unit] -trait Repository[-Key, Value] - extends ReadRepository[Key, Value] +trait GenericRepository[Eff[+_], -Key, Value] + extends GenericReadRepository[Eff, List, Key, Value, Unit] + with GenericWriteRepository[Eff, Key, Value] + +trait ReadRepository[-Key, +Value, -FilterArg] + extends GenericReadRepository[UIO, List, Key, Value, FilterArg]: + override def loadAll(ids: Seq[Key]): UIO[List[Value]] = + // Inefficient implementation, meant to be overridden + ZIO.foreach(ids)(load).map(_.flatten.toList) + +trait WriteRepository[-Key, -Value] + extends GenericWriteRepository[UIO, Key, Value] + +trait Repository[-Key, Value, -FilterArg] + extends ReadRepository[Key, Value, FilterArg] with WriteRepository[Key, Value] diff --git a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala index e2004d7..9420dbf 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala @@ -9,6 +9,5 @@ val layer: ULayer[FileStore] = ZLayer.succeed { new FileStore: override def store(file: FileRepr): Op[FileRef] = - for ref <- FileRef(file.name, "#").toZIO - yield ref + ZIO.succeed(FileRef.unsafe(file.name, "#")) } diff --git a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala index 3c83b59..e3b7d45 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala @@ -3,9 +3,15 @@ import zio.* -trait InMemoryRepository[Key, Value](data: Ref[Map[Key, Value]]) - extends Repository[Key, Value]: +trait InMemoryRepository[Key, Value, FilterArg]( + data: Ref[Map[Key, Value]], + filter: FilterArg => Value => Boolean +) extends Repository[Key, Value, FilterArg]: override def save(key: Key, value: Value): UIO[Unit] = data.update(_ + (key -> value)) - override def find(key: Key): UIO[Option[Value]] = + override def load(key: Key): UIO[Option[Value]] = data.get.map(_.get(key)) + override def loadAll(keys: Seq[Key]): UIO[List[Value]] = + data.get.map(_.view.filterKeys(keys.contains).values.toList) + override def find(filterArg: FilterArg): UIO[List[Value]] = + data.get.map(_.values.filter(filter(filterArg)).toList) diff --git a/tapir/js/src/main/scala/works/iterative/tapir/CustomTapirPlatformSpecific.scala b/tapir/js/src/main/scala/works/iterative/tapir/CustomTapirPlatformSpecific.scala index 5421105..32b3902 100644 --- a/tapir/js/src/main/scala/works/iterative/tapir/CustomTapirPlatformSpecific.scala +++ b/tapir/js/src/main/scala/works/iterative/tapir/CustomTapirPlatformSpecific.scala @@ -1,6 +1,7 @@ package works.iterative.tapir import zio.* +import zio.stream.* import sttp.tapir.client.sttp.SttpClientInterpreter import sttp.tapir.PublicEndpoint import sttp.tapir.client.sttp.WebSocketToPipe @@ -11,6 +12,9 @@ import org.scalajs.dom import sttp.client3.impl.zio.FetchZioBackend import sttp.tapir.DecodeResult +import works.iterative.core.FileSupport +import works.iterative.core.FileSupport.* +import sttp.model.Part trait CustomTapirPlatformSpecific extends SttpClientInterpreter: self: CustomTapir => @@ -26,6 +30,12 @@ ) ) + extension (f: FileRepr) + def toPart: Task[Part[Array[Byte]]] = + f.toStream.run(ZSink.collectAll).map { bytes => + Part("file", bytes.toArray, fileName = Some(f.name)) + } + def makeClient[I, E, O]( endpoint: PublicEndpoint[I, E, O, Any] )(using diff --git a/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala b/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala index 828fc6d..42ff9e1 100644 --- a/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala +++ b/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala @@ -11,4 +11,7 @@ extension [I, E, O](f: Client[I, E, O]) def apply(i: I): IO[E, O] = f(i) trait ClientEndpointFactory: + def umake[I, O]( + endpoint: PublicEndpoint[I, Unit, O, Any] + ): Client[I, Nothing, O] def make[I, E, O](endpoint: PublicEndpoint[I, E, O, Any]): Client[I, E, O] diff --git a/build.sbt b/build.sbt index 0aff685..98ec92b 100644 --- a/build.sbt +++ b/build.sbt @@ -42,6 +42,7 @@ excludeDependencies += // Gets transitively dragged in by zio-nio, conflicting with _3 ExclusionRule("org.scala-lang.modules", "scala-collection-compat_2.13") ) + .dependsOn(core) lazy val codecs = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) diff --git a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala index 0582cb5..16211a8 100644 --- a/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala +++ b/core/js/src/main/scala/works/iterative/core/service/impl/JsStorageRepository.scala @@ -7,10 +7,12 @@ // TODO: improve error reporting on generic repositories // This is good just for prototypes -class JsStorageRepository[Value: JsonCodec](storage: Storage) - extends Repository[String, Value]: +// And it cannot be used to query data, as there is no way to iterate the storage +class JsStorageRepository[Value: JsonCodec]( + storage: Storage +) extends Repository[String, Value, String]: - override def find(id: String): UIO[Option[Value]] = { + override def load(id: String): UIO[Option[Value]] = { for raw <- ZIO.attemptBlocking(Option(storage.getItem(id))) data <- ZIO.foreach(raw) { r => @@ -23,3 +25,5 @@ override def save(key: String, value: Value): UIO[Unit] = ZIO.attemptBlocking(storage.setItem(key, value.toJson)).orDie + + override def find(id: String): UIO[List[Value]] = load(id).map(_.toList) diff --git a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala index 273113e..43b1841 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/FileStore.scala @@ -2,15 +2,14 @@ import works.iterative.core.FileSupport.* import works.iterative.core.FileRef -import works.iterative.core.UserMessage import zio.* trait FileStore: - type Op[A] = IO[UserMessage, A] + type Op[A] = UIO[A] def store(file: FileRepr): Op[FileRef] object FileStore: - type Op[A] = ZIO[FileStore, UserMessage, A] + type Op[A] = URIO[FileStore, A] def store(file: FileRepr): Op[FileRef] = ZIO.serviceWithZIO(_.store(file)) diff --git a/core/shared/src/main/scala/works/iterative/core/service/Repository.scala b/core/shared/src/main/scala/works/iterative/core/service/Repository.scala index dc7d292..c945947 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/Repository.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/Repository.scala @@ -2,14 +2,30 @@ import zio.* -trait ReadRepository[-Key, +Value]: - type Op[A] = UIO[A] - def find(id: Key): Op[Option[Value]] +trait GenericReadRepository[Eff[+_], Coll[+_], -Key, +Value, -FilterArg]: + type Op[A] = Eff[A] -trait WriteRepository[-Key, -Value]: - type Op[A] = UIO[A] + def load(id: Key): Op[Option[Value]] + def loadAll(ids: Seq[Key]): Op[Coll[Value]] + def find(filter: FilterArg): Op[Coll[Value]] + +trait GenericWriteRepository[Eff[_], -Key, -Value]: + type Op[A] = Eff[A] def save(key: Key, value: Value): Op[Unit] -trait Repository[-Key, Value] - extends ReadRepository[Key, Value] +trait GenericRepository[Eff[+_], -Key, Value] + extends GenericReadRepository[Eff, List, Key, Value, Unit] + with GenericWriteRepository[Eff, Key, Value] + +trait ReadRepository[-Key, +Value, -FilterArg] + extends GenericReadRepository[UIO, List, Key, Value, FilterArg]: + override def loadAll(ids: Seq[Key]): UIO[List[Value]] = + // Inefficient implementation, meant to be overridden + ZIO.foreach(ids)(load).map(_.flatten.toList) + +trait WriteRepository[-Key, -Value] + extends GenericWriteRepository[UIO, Key, Value] + +trait Repository[-Key, Value, -FilterArg] + extends ReadRepository[Key, Value, FilterArg] with WriteRepository[Key, Value] diff --git a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala index e2004d7..9420dbf 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryFileStore.scala @@ -9,6 +9,5 @@ val layer: ULayer[FileStore] = ZLayer.succeed { new FileStore: override def store(file: FileRepr): Op[FileRef] = - for ref <- FileRef(file.name, "#").toZIO - yield ref + ZIO.succeed(FileRef.unsafe(file.name, "#")) } diff --git a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala index 3c83b59..e3b7d45 100644 --- a/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala +++ b/core/shared/src/main/scala/works/iterative/core/service/impl/InMemoryRepository.scala @@ -3,9 +3,15 @@ import zio.* -trait InMemoryRepository[Key, Value](data: Ref[Map[Key, Value]]) - extends Repository[Key, Value]: +trait InMemoryRepository[Key, Value, FilterArg]( + data: Ref[Map[Key, Value]], + filter: FilterArg => Value => Boolean +) extends Repository[Key, Value, FilterArg]: override def save(key: Key, value: Value): UIO[Unit] = data.update(_ + (key -> value)) - override def find(key: Key): UIO[Option[Value]] = + override def load(key: Key): UIO[Option[Value]] = data.get.map(_.get(key)) + override def loadAll(keys: Seq[Key]): UIO[List[Value]] = + data.get.map(_.view.filterKeys(keys.contains).values.toList) + override def find(filterArg: FilterArg): UIO[List[Value]] = + data.get.map(_.values.filter(filter(filterArg)).toList) diff --git a/tapir/js/src/main/scala/works/iterative/tapir/CustomTapirPlatformSpecific.scala b/tapir/js/src/main/scala/works/iterative/tapir/CustomTapirPlatformSpecific.scala index 5421105..32b3902 100644 --- a/tapir/js/src/main/scala/works/iterative/tapir/CustomTapirPlatformSpecific.scala +++ b/tapir/js/src/main/scala/works/iterative/tapir/CustomTapirPlatformSpecific.scala @@ -1,6 +1,7 @@ package works.iterative.tapir import zio.* +import zio.stream.* import sttp.tapir.client.sttp.SttpClientInterpreter import sttp.tapir.PublicEndpoint import sttp.tapir.client.sttp.WebSocketToPipe @@ -11,6 +12,9 @@ import org.scalajs.dom import sttp.client3.impl.zio.FetchZioBackend import sttp.tapir.DecodeResult +import works.iterative.core.FileSupport +import works.iterative.core.FileSupport.* +import sttp.model.Part trait CustomTapirPlatformSpecific extends SttpClientInterpreter: self: CustomTapir => @@ -26,6 +30,12 @@ ) ) + extension (f: FileRepr) + def toPart: Task[Part[Array[Byte]]] = + f.toStream.run(ZSink.collectAll).map { bytes => + Part("file", bytes.toArray, fileName = Some(f.name)) + } + def makeClient[I, E, O]( endpoint: PublicEndpoint[I, E, O, Any] )(using diff --git a/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala b/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala index 828fc6d..42ff9e1 100644 --- a/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala +++ b/tapir/shared/src/main/scala/works/iterative/tapir/ClientEndpointFactory.scala @@ -11,4 +11,7 @@ extension [I, E, O](f: Client[I, E, O]) def apply(i: I): IO[E, O] = f(i) trait ClientEndpointFactory: + def umake[I, O]( + endpoint: PublicEndpoint[I, Unit, O, Any] + ): Client[I, Nothing, O] def make[I, E, O](endpoint: PublicEndpoint[I, E, O, Any]): Client[I, E, O] diff --git a/tapir/shared/src/main/scala/works/iterative/tapir/LiveClientEndpointFactory.scala b/tapir/shared/src/main/scala/works/iterative/tapir/LiveClientEndpointFactory.scala index 208b904..647a6fd 100644 --- a/tapir/shared/src/main/scala/works/iterative/tapir/LiveClientEndpointFactory.scala +++ b/tapir/shared/src/main/scala/works/iterative/tapir/LiveClientEndpointFactory.scala @@ -10,6 +10,14 @@ ) extends ClientEndpointFactory with CustomTapir: + override def umake[I, O]( + endpoint: PublicEndpoint[I, Unit, O, Any] + ): Client[I, Nothing, O] = Client((input: I) => + mkClient(endpoint)(input).orDieWith(_ => + new IllegalStateException("Infallible endpoint failed") + ) + ) + override def make[I, E, O]( endpoint: PublicEndpoint[I, E, O, Any] ): Client[I, E, O] = mkClient(endpoint)