• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pureconfig / pureconfig / 16844894943

09 Aug 2025 03:28AM UTC coverage: 94.686% (-0.04%) from 94.726%
16844894943

Pull #1838

web-flow
Merge 12f0062cf into 38f73c635
Pull Request #1838: Rid of deprecated URL constructor

1 of 1 new or added line in 1 file covered. (100.0%)

95 existing lines in 26 files now uncovered.

2744 of 2898 relevant lines covered (94.69%)

2.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.33
/modules/cats-effect2/src/main/scala/pureconfig/module/catseffect2/package.scala
1
package pureconfig.module
2

3
import java.io.OutputStream
4
import java.nio.charset.StandardCharsets
5
import java.nio.file.{Files, Path}
6

7
import scala.reflect.ClassTag
8

9
import cats.data.EitherT
10
import cats.effect.{Blocker, ContextShift, Resource, Sync}
11
import cats.implicits._
12
import com.typesafe.config.ConfigRenderOptions
13

14
import pureconfig._
15
import pureconfig.error.ConfigReaderException
16

17
package object catseffect2 {
18

19
  /** Load a configuration of type `A` from a config source
20
    *
21
    * @param cs
22
    *   the config source from where the configuration will be loaded
23
    * @param blocker
24
    *   the blocking context which will be used to load the configuration.
25
    * @return
26
    *   The returned action will complete with `A` if it is possible to create an instance of type `A` from the
27
    *   configuration source, or fail with a ConfigReaderException which in turn contains details on why it isn't
28
    *   possible
29
    */
30
  def loadF[F[_]: Sync: ContextShift, A](
1✔
31
      cs: ConfigSource,
32
      blocker: Blocker
33
  )(implicit reader: ConfigReader[A], ct: ClassTag[A]): F[A] =
34
    EitherT(blocker.delay(cs.cursor()))
1✔
35
      .subflatMap(reader.from)
1✔
36
      .leftMap(ConfigReaderException[A])
37
      .rethrowT
1✔
38

39
  /** Load a configuration of type `A` from the standard configuration files
40
    *
41
    * @param blocker
42
    *   the blocking context which will be used to load the configuration.
43
    * @return
44
    *   The returned action will complete with `A` if it is possible to create an instance of type `A` from the
45
    *   configuration files, or fail with a ConfigReaderException which in turn contains details on why it isn't
46
    *   possible
47
    */
48
  def loadConfigF[F[_]: Sync: ContextShift, A: ConfigReader](blocker: Blocker)(implicit ct: ClassTag[A]): F[A] =
1✔
49
    loadF(ConfigSource.default, blocker)
2✔
50

51
  /** Save the given configuration into a property file
52
    *
53
    * @param conf
54
    *   The configuration to save
55
    * @param outputPath
56
    *   Where to write the configuration
57
    * @param blocker
58
    *   the blocking context which will be used to load the configuration.
59
    * @param overrideOutputPath
60
    *   Override the path if it already exists
61
    * @param options
62
    *   the config rendering options
63
    * @return
64
    *   The return action will save out the supplied configuration upon invocation
65
    */
UNCOV
66
  def blockingSaveConfigAsPropertyFileF[F[_]: ContextShift, A: ConfigWriter](
67
      conf: A,
68
      outputPath: Path,
69
      blocker: Blocker,
70
      overrideOutputPath: Boolean = false,
UNCOV
71
      options: ConfigRenderOptions = ConfigRenderOptions.defaults()
72
  )(implicit F: Sync[F]): F[Unit] = {
73
    val fileAlreadyExists =
74
      F.raiseError[Unit](
×
75
        new IllegalArgumentException(s"Cannot save configuration in file '$outputPath' because it already exists")
×
76
      )
77

78
    val fileIsDirectory =
79
      F.raiseError[Unit](
×
80
        new IllegalArgumentException(s"Cannot save configuration in file '$outputPath' because it is a directory")
×
81
      )
82

83
    val check =
84
      F.defer {
×
85
        if (!overrideOutputPath && Files.isRegularFile(outputPath)) fileAlreadyExists
×
86
        else if (Files.isDirectory(outputPath)) fileIsDirectory
×
87
        else F.unit
×
88
      }
89

90
    val outputStream =
91
      Resource.make(blocker.delay(Files.newOutputStream(outputPath))) { os =>
×
UNCOV
92
        blocker.delay(os.close())
93
      }
94

95
    blocker.blockOn(check) >> outputStream.use { os =>
×
UNCOV
96
      blockingSaveConfigToStreamF(conf, os, blocker, options)
97
    }
98
  }
99

100
  /** Writes the configuration to the output stream and closes the stream
101
    *
102
    * @param conf
103
    *   The configuration to write
104
    * @param outputStream
105
    *   The stream in which the configuration should be written
106
    * @param blocker
107
    *   the blocking context which will be used to load the configuration.
108
    * @param options
109
    *   the config rendering options
110
    * @return
111
    *   The return action will save out the supplied configuration upon invocation
112
    */
113
  def blockingSaveConfigToStreamF[F[_]: ContextShift, A](
1✔
114
      conf: A,
115
      outputStream: OutputStream,
116
      blocker: Blocker,
117
      options: ConfigRenderOptions = ConfigRenderOptions.defaults()
1✔
118
  )(implicit F: Sync[F], writer: ConfigWriter[A]): F[Unit] =
119
    F.delay(writer.to(conf)).map { rawConf =>
2✔
120
      // HOCON requires UTF-8:
121
      // https://github.com/lightbend/config/blob/master/HOCON.md#unchanged-from-json
122
      StandardCharsets.UTF_8.encode(rawConf.render(options)).array
2✔
123
    } flatMap { bytes =>
1✔
124
      blocker.delay {
2✔
125
        outputStream.write(bytes)
1✔
126
        outputStream.flush()
1✔
127
      }
128
    }
129

130
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc