• 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

53.57
/modules/cats-effect/src/main/scala/pureconfig/module/catseffect/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, NonEmptyList}
10
import cats.effect.{Resource, Sync}
11
import cats.implicits._
12
import com.typesafe.config.{Config => TypesafeConfig, ConfigRenderOptions}
13

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

17
package object catseffect {
18

19
  @deprecated("Root will be treated as the default namespace", "0.12.0")
20
  val defaultNameSpace = ""
1✔
21

22
  /** Load a configuration of type `A` from a config source
23
    *
24
    * @param cs
25
    *   the config source from where the configuration will be loaded
26
    * @return
27
    *   The returned action will complete with `A` if it is possible to create an instance of type `A` from the
28
    *   configuration source, or fail with a ConfigReaderException which in turn contains details on why it isn't
29
    *   possible
30
    */
31
  def loadF[F[_], A](
1✔
32
      cs: ConfigSource
33
  )(implicit F: Sync[F], reader: ConfigReader[A], ct: ClassTag[A]): F[A] =
34
    EitherT(F.blocking(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
    * @return
42
    *   The returned action will complete with `A` if it is possible to create an instance of type `A` from the
43
    *   configuration files, or fail with a ConfigReaderException which in turn contains details on why it isn't
44
    *   possible
45
    */
46
  def loadConfigF[F[_], A](implicit F: Sync[F], reader: ConfigReader[A], ct: ClassTag[A]): F[A] =
1✔
47
    loadF(ConfigSource.default)
2✔
48

49
  /** Load a configuration of type `A` from the standard configuration files
50
    *
51
    * @param namespace
52
    *   the base namespace from which the configuration should be load
53
    * @return
54
    *   The returned action will complete with `A` if it is possible to create an instance of type `A` from the
55
    *   configuration files, or fail with a ConfigReaderException which in turn contains details on why it isn't
56
    *   possible
57
    */
58
  @deprecated("Use `ConfigSource.default.at(namespace).loadF[F, A]` instead", "0.12.0")
UNCOV
59
  def loadConfigF[F[_], A](
60
      namespace: String
61
  )(implicit F: Sync[F], reader: ConfigReader[A], ct: ClassTag[A]): F[A] =
62
    loadF[F, A](ConfigSource.default.at(namespace))
×
63

64
  /** Load a configuration of type `A` from the given file. Note that standard configuration files are still loaded but
65
    * can be overridden from the given configuration file
66
    *
67
    * @param path
68
    *   the path of the configuration file from which to load
69
    * @return
70
    *   The returned action will complete with `A` if it is possible to create an instance of type `A` from the
71
    *   configuration file, or fail with a ConfigReaderException which in turn contains details on why it isn't possible
72
    */
73
  @deprecated("Use `ConfigSource.default(ConfigSource.file(path)).loadF[F, A]` instead", "0.12.0")
UNCOV
74
  def loadConfigF[F[_], A](
75
      path: Path
76
  )(implicit F: Sync[F], reader: ConfigReader[A], ct: ClassTag[A]): F[A] =
77
    loadF[F, A](ConfigSource.default(ConfigSource.file(path)))
×
78

79
  /** Load a configuration of type `A` from the given file. Note that standard configuration files are still loaded but
80
    * can be overridden from the given configuration file
81
    *
82
    * @param path
83
    *   the path of the configuration file from which to load
84
    * @param namespace
85
    *   the base namespace from which the configuration should be load
86
    * @return
87
    *   The returned action will complete with `A` if it is possible to create an instance of type `A` from the
88
    *   configuration file, or fail with a ConfigReaderException which in turn contains details on why it isn't possible
89
    */
90
  @deprecated("Use `ConfigSource.default(ConfigSource.file(path)).at(namespace).loadF[F, A]` instead", "0.12.0")
UNCOV
91
  def loadConfigF[F[_], A](path: Path, namespace: String)(implicit
92
      F: Sync[F],
93
      reader: ConfigReader[A],
94
      ct: ClassTag[A]
95
  ): F[A] =
96
    loadF[F, A](ConfigSource.default(ConfigSource.file(path)).at(namespace))
×
97

98
  /** Load a configuration of type `A` from the given `Config`
99
    * @return
100
    *   The returned action will complete with `A` if it is possible to create an instance of type `A` from the
101
    *   configuration object, or fail with a ConfigReaderException which in turn contains details on why it isn't
102
    *   possible
103
    */
104
  @deprecated("Use `ConfigSource.fromConfig(conf).loadF[F, A]` instead", "0.12.0")
UNCOV
105
  def loadConfigF[F[_], A](
106
      conf: TypesafeConfig
107
  )(implicit F: Sync[F], reader: ConfigReader[A], ct: ClassTag[A]): F[A] =
108
    loadF[F, A](ConfigSource.fromConfig(conf))
×
109

110
  /** Load a configuration of type `A` from the given `Config`
111
    * @return
112
    *   The returned action will complete with `A` if it is possible to create an instance of type `A` from the
113
    *   configuration object, or fail with a ConfigReaderException which in turn contains details on why it isn't
114
    *   possible
115
    */
116
  @deprecated("Use `ConfigSource.fromConfig(conf).at(namespace).loadF[F, A]` instead", "0.12.0")
UNCOV
117
  def loadConfigF[F[_], A](conf: TypesafeConfig, namespace: String)(implicit
118
      F: Sync[F],
119
      reader: ConfigReader[A],
120
      ct: ClassTag[A]
121
  ): F[A] =
122
    loadF[F, A](ConfigSource.fromConfig(conf).at(namespace))
×
123

124
  /** Save the given configuration into a property file
125
    *
126
    * @param conf
127
    *   The configuration to save
128
    * @param outputPath
129
    *   Where to write the configuration
130
    * @param overrideOutputPath
131
    *   Override the path if it already exists
132
    * @param options
133
    *   the config rendering options
134
    * @return
135
    *   The return action will save out the supplied configuration upon invocation
136
    */
UNCOV
137
  def saveConfigAsPropertyFileF[F[_], A](
138
      conf: A,
139
      outputPath: Path,
140
      overrideOutputPath: Boolean = false,
UNCOV
141
      options: ConfigRenderOptions = ConfigRenderOptions.defaults()
142
  )(implicit F: Sync[F], writer: ConfigWriter[A]): F[Unit] = {
143
    val fileAlreadyExists: F[Unit] =
144
      F.raiseError(
×
145
        new IllegalArgumentException(s"Cannot save configuration in file '$outputPath' because it already exists")
×
146
      )
147

148
    val fileIsDirectory: F[Unit] =
149
      F.raiseError(
×
150
        new IllegalArgumentException(s"Cannot save configuration in file '$outputPath' because it is a directory")
×
151
      )
152

153
    val check =
UNCOV
154
      F.blocking(!overrideOutputPath && Files.isRegularFile(outputPath))
155
        .ifM(fileAlreadyExists, F.blocking(Files.isDirectory(outputPath)).ifM(fileIsDirectory, F.unit))
×
156

157
    val outputStream = Resource.fromAutoCloseable(F.blocking(Files.newOutputStream(outputPath)))
×
158

159
    check >> outputStream.use { os =>
×
UNCOV
160
      saveConfigToStreamF(conf, os, options)
161
    }
162
  }
163

164
  /** Writes the configuration to the output stream and closes the stream
165
    *
166
    * @param conf
167
    *   The configuration to write
168
    * @param outputStream
169
    *   The stream in which the configuration should be written
170
    * @param options
171
    *   the config rendering options
172
    * @return
173
    *   The return action will save out the supplied configuration upon invocation
174
    */
175
  def saveConfigToStreamF[F[_], A](
1✔
176
      conf: A,
177
      outputStream: OutputStream,
178
      options: ConfigRenderOptions = ConfigRenderOptions.defaults()
1✔
179
  )(implicit F: Sync[F], writer: ConfigWriter[A]): F[Unit] =
180
    F.delay(writer.to(conf)).map { rawConf =>
2✔
181
      // HOCON requires UTF-8:
182
      // https://github.com/lightbend/config/blob/master/HOCON.md#unchanged-from-json
183
      StandardCharsets.UTF_8.encode(rawConf.render(options)).array
2✔
184
    } flatMap { bytes =>
1✔
185
      F.blocking {
2✔
186
        outputStream.write(bytes)
2✔
187
        outputStream.flush()
2✔
188
      }
189
    }
190

191
  /** Loads `files` in order, allowing values in later files to backstop missing values from prior, and converts them
192
    * into a `A`.
193
    *
194
    * This is a convenience method which enables having default configuration which backstops local configuration.
195
    *
196
    * Note: If an element of `files` references a file which doesn't exist or can't be read, it will silently be
197
    * ignored.
198
    *
199
    * @param files
200
    *   Files ordered in decreasing priority containing part or all of a `A`. Must not be empty.
201
    */
202
  @deprecated("Construct a custom `ConfigSource` pipeline instead", "0.12.0")
UNCOV
203
  def loadConfigFromFilesF[F[_], A](
204
      files: NonEmptyList[Path]
205
  )(implicit F: Sync[F], reader: ConfigReader[A], ct: ClassTag[A]): F[A] =
206
    loadF[F, A](
×
UNCOV
207
      ConfigSource.default(
208
        files
UNCOV
209
          .map(ConfigSource.file(_).optional)
UNCOV
210
          .foldLeft(ConfigSource.empty)(_.withFallback(_))
211
      )
212
    )
213
}
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