Skip to content

Instantly share code, notes, and snippets.

@chuwy
Forked from DiggesT/Api.scala
Created August 11, 2023 10:06
Show Gist options
  • Save chuwy/b0f6b2d8df974dd10a9fa2d2e9533272 to your computer and use it in GitHub Desktop.
Save chuwy/b0f6b2d8df974dd10a9fa2d2e9533272 to your computer and use it in GitHub Desktop.
Scala code research
def loadFromPath[F[_]: Sync](path: JPath): F[Config] =
val source = ConfigSource.default(ConfigSource.file(path)).withFallback(ConfigSource.default)
loadF[F, Config](source)
def load(args: List[String]): IO[Either[Help, (SubCommand, Option[Config])]] = //input array: List[String], output: why IO
command.parse(args) match // how it matching string with help/commands?
case Left(help) => IO.pure(help.asLeft) //help command
case Right(cli) => //other commands
cli match //match with SubCommand
case SubCommand.Run(configPath, _) => //Run command
loadFromPath[IO](configPath).map(c => (cli, Some(c)).asRight)
case SubCommand.Load(configPath, _) => //Load command
loadFromPath[IO](configPath).map(c => (cli, Some(c)).asRight)
case SubCommand.ApiExport(path) => //ApiExport command
IO.pure(Right(SubCommand.ApiExport(path), None))
object Main extends IOApp:
def run(args: List[String]): IO[ExitCode] = //function, input list, output IO
greet //print version
*> //
Config. //load config file
load(args). //pattern matching the input command
flatMap { //the inner grouping of an item is removed and a sequence is generated
case Right((Config.SubCommand.Load(_, fixtures), Some(config))) => //subcommand "Load" matched with Some parameters
Resources. //excexute Load command with parameters
build[IO](config) //can't understand this definition: def build[F[_]: Async: Trace: Network: Console]
.use{ resources => //use?
import resources.given //something like lambda function
//using for load fixtures
val path = Path.fromNioPath(fixtures)
Fixtures.load[IO](path)
}.as(ExitCode.Success)// why casting success type? execute command?
case Right((run: Config.SubCommand.Run, Some(config))) => //subcommand "Run" matched
config.database match //database type matching
case _: Config.Database.Postgres if run.fixtures.isDefined => //fixtures are defined
IO.println("It is prohibited to autoload fixtures with Postgres in config, use `load` command instead") .as(ExitCode.Error)//should use another command, casting as error, can't excute command
case _ =>
Resources.//excexute Run command with parameters
build[IO](config).use { resources => //same build function
import resources.given
Server.server(config).use { _ =>
run.fixtures.traverse_(jpath => Fixtures.load[IO](Path.fromNioPath(jpath))) *> IO.never
}
}.as(ExitCode.Success)// why casting success type?
case Right((Config.SubCommand.ApiExport(path), None)) => //subcommand "ApiExport" matched
Server.exportApi[IO](Path.fromNioPath(path)).as(ExitCode.Success)
case Right(other) => //undefined command
IO.println(s"$other is not expected").as(ExitCode.Error)//print error text, why casting error type? can't execute command?
case Left(help) => //help command
IO.println(help.toString).as(ExitCode.Error) //help returning from load(), ptint help, why casting error type?
}
def greet: IO[Unit] =
IO.println(s"Running Ratio ${BuildInfo.version}...") // IO used for execute println only when greet called
case class Resources[F[_]](random: Random[F],
passwords: Passwords[F],
persons: Persons[F],
tasks: Tasks[F],
session: Session[F],
inventory: Inventory[F],
blobs: Blobs[F],
comments: Comments[F],
fixtures: Fixtures[F],
events: Events[F],
bus: Bus[F],
eventStorage: EventStorage[F],
chats: Chats[F]):
given Random[F] = random
given Persons[F] = persons
given Tasks[F] = tasks
given Inventory[F] = inventory
given Session[F] = session
given Blobs[F] = blobs
given Comments[F] = comments
given Passwords[F] = passwords
given Fixtures[F] = fixtures
given Events[F] = events
given Bus[F] = bus
given EventStorage[F] = eventStorage
given Chats[F] = chats
object Resources:
def build[F[_]: Async: Trace: Network: Console](config: Config): Resource[F, Resources[F]] = //can't understand function definition
for { //sequence comprehensions
//variables for Resources
random <- Resource.eval(Random.scalaUtilRandom[F])
passwords = auth.Service.argon[F](random, config.auth.pepper)
_ <- Resource.eval(precreate[F](List(config.storage._1, config.storage._2, config.storage._3))) //what does it mean _?
a <- buildDb[F](random, passwords, config.database, config.storage) //includes fields: persons ... chats
_ <- notifications.Service.run[F](config.notifications.flatMap(_.telegram), a._9, a._1, a._11).background //what does it mean _?
} yield Resources(random, passwords, a._1, a._2, a._3, a._4, a._5, a._6, a._7, a._8, a._9, a._10, a._11) //return Resource[F, Resources[F]] type
def mkPostgresF[F[_]: Async: Files: Clock: Console: Network: Trace: Random: Passwords](storage: Config.Storage)(session: Resource[F, DbSession[F]]) =
for {
p <- Resource.eval(users.Postgres.build[F](session))
t <- Resource.pure(tasks.Postgres.build[F](session))
s <- Resource.eval(Session.inMemory[F])
i <- Resource.pure(inventory.Postgres.build[F](session))
b <- Resource.pure(blobs.Postgres.build[F](blobs.Service.read(storage), blobs.Service.write(storage), session))
c <- Resource.pure(comments.Postgres.build[F](session))
f <- Resource.pure(Fixtures.postgres[F](session))
e <- Resource.pure(calendar.Postgres.build[F](session))
eb <- bus.Postgres.build[F](session)
es <- Resource.pure(bus.Postgres.buildStorage(session))
ch <- Resource.pure(notifications.Postgres.build[F](session))
_ <- eb.subscribe.evalTap(es.add).compile.drain.background
} yield (p, t, s, i, b, c, f, e, eb, es, ch)
def inMemory[F[_]: Concurrent: Passwords: Random: Clock: Files: UUIDGen: Console](storage: Config.Storage) =
for {
p <- Persons.inMemory[F].map(_._2)
t <- Tasks.inMemory[F].map(_._2)
s <- Session.inMemory[F]
i <- Inventory.inMemory[F].map(_._2)
b <- Blobs.inMemory[F](blobs.Service.read(storage), blobs.Service.write(storage)).map(_._2)
c <- Comments.inMemory[F].map(_._2)
f = Fixtures.inMemory[F]
e <- Events.inMemory[F].map(_._2)
eb <- Bus.inMemory[F]
es <- EventStorage.inMemory[F].map(_._2)
ch <- Chats.inMemory[F].map(_._2)
} yield (p, t, s, i, b, c, f, e, eb, es, ch)
def buildDb[F[_]: Async: Files: Clock: Console: Network: Trace](random: Random[F],
passwords: Passwords[F],
database: Config.Database,
storage: Config.Storage): Resource[F, (Persons[F], Tasks[F], Session[F], Inventory[F], Blobs[F], Comments[F], Fixtures[F], Events[F], Bus[F], EventStorage[F], Chats[F])] =
given Random[F] = random
given Passwords[F] = passwords
val resources = database match
case pg: Config.Database.Postgres =>
Postgres.build[F](pg).flatMap(mkPostgresF[F](storage))
case Config.Database.InMemory =>
Resource.eval(inMemory[F](storage))
resources
def precreate[F[_]: Files: Monad](paths: List[Path]): F[Unit] =
paths.traverse_ { path => Files[F].exists(path).ifM(Monad[F].unit, Files[F].createDirectories(path)) }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment