Skip to content

Commit

Permalink
Change config path
Browse files Browse the repository at this point in the history
Closes #116.
  • Loading branch information
kubukoz committed Sep 3, 2021
1 parent 54e9afd commit 16bad73
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 10 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ This will create a launcher at `./target/universal/stage/bin/spotify-next`.
## Usage

The application requires some configuration (e.g. the client ID for the Spotify Web API).
It's stored in a file at `~/.spotify-next.json`.
It's stored in a file at `$XDG_CONFIG_HOME/spotify-next/config.json` or `$HOME/.config/spotify-next/config.json` (whichever works first).
When you first run the application, or if that file is deleted, the application will ask and attempt to create one.

The configuration defines the port for the embedded HTTP server used for authentication. The server will only start when the login flow is triggered, and stop afterwards.
Expand Down
7 changes: 4 additions & 3 deletions src/main/scala/com/kubukoz/next/ConfigLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import cats.effect.std.Console
import fs2.io.file.Files
import cats.FlatMap
import cats.MonadThrow
import fs2.Pipe

trait ConfigLoader[F[_]] {
def saveConfig(config: Config): F[Unit]
Expand Down Expand Up @@ -58,9 +59,9 @@ object ConfigLoader {
def default[F[_]: Files: MonadThrow](configPath: Path)(using fs2.Compiler[F, F]): ConfigLoader[F] =
new ConfigLoader[F] {

private val createOrOverwriteFile =
Files[F]
.writeAll(configPath, List(StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))
private val createOrOverwriteFile: Pipe[F, Byte, Nothing] = bytes =>
fs2.Stream.exec(Files[F].createDirectories(configPath.getParent).void) ++
bytes.through(Files[F].writeAll(configPath, List(StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)))

def saveConfig(config: Config): F[Unit] =
fs2
Expand Down
42 changes: 36 additions & 6 deletions src/main/scala/com/kubukoz/next/Program.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.kubukoz.next

import cats.effect.Concurrent
import cats.effect.Sync
import cats.effect.MonadCancelThrow
import cats.effect.Resource
import cats.effect.kernel.Async
Expand All @@ -20,15 +21,44 @@ import org.http4s.client.middleware.RequestLogger
import org.http4s.client.middleware.ResponseLogger
import java.lang.System
import java.nio.file.Paths
import java.nio.file.Path
import cats.data.OptionT
import cats.MonadThrow

object Program {
val configPath = Paths.get(System.getProperty("user.home")).resolve(".spotify-next.json")

def makeLoader[F[_]: Files: Ref.Make: UserOutput: Console: fs2.Compiler.Target]: F[ConfigLoader[F]] =
ConfigLoader
.cached[F]
.compose(ConfigLoader.withCreateFileIfMissing[F](configPath))
.apply(ConfigLoader.default[F](configPath))
trait System[F[_]] {
def getenv(name: String): F[Option[String]]
}

object System {
def apply[F[_]](using F: System[F]): System[F] = F

given [F[_]: Sync]: System[F] = name => Sync[F].delay(java.lang.System.getenv(name)).map(Option(_))
}

private def configPath[F[_]: System: MonadThrow]: F[Path] =
OptionT(System[F].getenv("XDG_CONFIG_HOME"))
.map(Paths.get(_))
.getOrElseF(
System[F]
.getenv("HOME")
.flatMap(_.liftTo[F](new Throwable("HOME not defined, I don't even")))
.map(Paths.get(_))
.map(_.resolve(".config"))
)
.map(
_.resolve("spotify-next")
.resolve("config.json")
)

def makeLoader[F[_]: Files: System: Ref.Make: UserOutput: Console: fs2.Compiler.Target]: F[ConfigLoader[F]] =
configPath[F].flatMap { p =>
ConfigLoader
.cached[F]
.compose(ConfigLoader.withCreateFileIfMissing[F](p))
.apply(ConfigLoader.default[F](p))
}

def makeBasicClient[F[_]: Async]: Resource[F, Client[F]] =
Resource
Expand Down

0 comments on commit 16bad73

Please sign in to comment.