(minor) Version, Doc, and Minor updates. (#2)
All checks were successful
/ Build and Release Library (push) Successful in 2m11s
All checks were successful
/ Build and Release Library (push) Successful in 2m11s
Reviewed-on: #2
This commit is contained in:
parent
f127645860
commit
e44334b364
15 changed files with 113 additions and 16 deletions
|
@ -64,5 +64,6 @@ jobs:
|
|||
sbtn coverageOff
|
||||
sbtn clean
|
||||
sbtn compile
|
||||
sbtn doc
|
||||
sbtn publish
|
||||
fi
|
||||
|
|
|
@ -71,6 +71,7 @@ jobs:
|
|||
sbtn coverageOff
|
||||
sbtn clean
|
||||
sbtn semVerWriteVersionToFile
|
||||
sbtn doc
|
||||
sbtn publish
|
||||
fi
|
||||
- name: 'Create Git Tag'
|
||||
|
|
13
build.sbt
13
build.sbt
|
@ -1,4 +1,4 @@
|
|||
val scala3: String = "3.4.1"
|
||||
val scala3: String = "3.6.4"
|
||||
|
||||
externalResolvers := Seq(
|
||||
"Garrity Software Mirror" at "https://maven.garrity.co/releases",
|
||||
|
@ -10,13 +10,16 @@ ThisBuild / versionScheme := Some("semver-spec")
|
|||
ThisBuild / gsProjectName := "gs-config"
|
||||
|
||||
lazy val sharedSettings = Seq(
|
||||
scalaVersion := scala3,
|
||||
version := semVerSelected.value
|
||||
scalaVersion := scala3,
|
||||
version := semVerSelected.value,
|
||||
coverageFailOnMinimum := true,
|
||||
coverageMinimumStmtTotal := 100,
|
||||
coverageMinimumBranchTotal := 100
|
||||
)
|
||||
|
||||
lazy val testSettings = Seq(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scalameta" %% "munit" % "1.0.0-RC1" % Test
|
||||
"org.scalameta" %% "munit" % "1.1.0" % Test
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -27,6 +30,6 @@ lazy val `gs-config` = project
|
|||
.settings(name := s"${gsProjectName.value}-v${semVerMajor.value}")
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.typelevel" %% "cats-effect" % "3.5.2"
|
||||
"org.typelevel" %% "cats-effect" % "3.5.7"
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1 +1 @@
|
|||
sbt.version=1.9.9
|
||||
sbt.version=1.10.11
|
||||
|
|
|
@ -28,6 +28,6 @@ externalResolvers := Seq(
|
|||
"Garrity Software Releases" at "https://maven.garrity.co/gs"
|
||||
)
|
||||
|
||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.11")
|
||||
addSbtPlugin("gs" % "sbt-garrity-software" % "0.3.0")
|
||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.3.1")
|
||||
addSbtPlugin("gs" % "sbt-garrity-software" % "0.5.0")
|
||||
addSbtPlugin("gs" % "sbt-gs-semver" % "0.3.0")
|
||||
|
|
|
@ -9,7 +9,7 @@ import gs.config.v0.source.ConfigSource
|
|||
import gs.config.v0.source.EnvironmentConfigSource
|
||||
import gs.config.v0.source.MemoryConfigSource
|
||||
|
||||
/** Implementation of [[gs.config.Configuration]] that tracks every call to
|
||||
/** Implementation of [[gs.config.v0.Configuration]] that tracks every call to
|
||||
* `getValue` and reports on whether the query succeeded or failed.
|
||||
*
|
||||
* @param sources
|
||||
|
@ -23,6 +23,8 @@ final class AuditedConfiguration[F[_]: Sync](
|
|||
) extends BaseConfiguration[F]:
|
||||
import AuditedConfiguration.Acc
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def getValue[A: Configurable](
|
||||
key: ConfigKey[A]
|
||||
): F[Either[ConfigError, A]] =
|
||||
|
@ -93,7 +95,8 @@ object AuditedConfiguration:
|
|||
* sources.
|
||||
*
|
||||
* @param sources
|
||||
* The list of [[ConfigSource]] backing this configuration.
|
||||
* The list of [[gs.config.v0.source.ConfigSource]] backing this
|
||||
* configuration.
|
||||
* @return
|
||||
* The new [[Configuration]] instance.
|
||||
*/
|
||||
|
@ -109,26 +112,73 @@ object AuditedConfiguration:
|
|||
)
|
||||
)
|
||||
|
||||
/** Start building a new [[AuditedConfiguration]] for some source.
|
||||
*
|
||||
* @param source
|
||||
* The [[gs.config.v0.source.ConfigSource]] backing this configuration.
|
||||
* @return
|
||||
* New builder based on the given source.
|
||||
*/
|
||||
def forSource[F[_]: Sync](source: ConfigSource[F]): Builder[F] =
|
||||
Builder[F](NonEmptyList.of(source))
|
||||
|
||||
/** Start building a new [[AuditedConfiguration]] based on the environment.
|
||||
*
|
||||
* @return
|
||||
* New builder based on the environment.
|
||||
*/
|
||||
def forEnvironmentSource[F[_]: Sync]: Builder[F] =
|
||||
Builder[F](NonEmptyList.of(new EnvironmentConfigSource[F]))
|
||||
|
||||
/** Start building a new [[AuditedConfiguration]] based on the given in-memory
|
||||
* map of values.
|
||||
*
|
||||
* @param configs
|
||||
* The configuration map.
|
||||
* @return
|
||||
* New builder based on the config map.
|
||||
*/
|
||||
def forMemorySource[F[_]: Sync](configs: Map[String, String]): Builder[F] =
|
||||
Builder[F](NonEmptyList.of(new MemoryConfigSource[F](configs)))
|
||||
|
||||
/** Builder for [[AuditedConfiguration]].
|
||||
*
|
||||
* @param sources
|
||||
* The (non-empty) list of sources for the eventual configuration.
|
||||
*/
|
||||
case class Builder[F[_]: Sync](sources: NonEmptyList[ConfigSource[F]]):
|
||||
|
||||
/** Add the environment as a source to this builder.
|
||||
*
|
||||
* @return
|
||||
* This builder.
|
||||
*/
|
||||
def withEnvironmentSource(): Builder[F] =
|
||||
copy(sources = this.sources.append(new EnvironmentConfigSource[F]))
|
||||
|
||||
/** Add a memory map as a source to this builder.
|
||||
*
|
||||
* @param configs
|
||||
* The configuration map.
|
||||
* @return
|
||||
* This builder.
|
||||
*/
|
||||
def withMemorySource(configs: Map[String, String]): Builder[F] =
|
||||
copy(sources = this.sources.append(new MemoryConfigSource[F](configs)))
|
||||
|
||||
/** Add the given source to this builder.
|
||||
*
|
||||
* @param source
|
||||
* The [[gs.config.v0.source.ConfigSource]] to add.
|
||||
* @return
|
||||
* This builder.
|
||||
*/
|
||||
def withSource(source: ConfigSource[F]): Builder[F] =
|
||||
copy(sources = this.sources.append(source))
|
||||
|
||||
/** @return
|
||||
* A new [[AuditedConfiguration]] based on the configured sources.
|
||||
*/
|
||||
def build(): F[AuditedConfiguration[F]] =
|
||||
ConfigManifest
|
||||
.standard[F]
|
||||
|
|
|
@ -11,7 +11,7 @@ abstract class BaseConfiguration[F[_]] extends Configuration[F]:
|
|||
): Either[ConfigError, A] =
|
||||
key match
|
||||
case ConfigKey.WithDefaultValue(_, defaultValue) =>
|
||||
Right(defaultValue)
|
||||
Right(defaultValue())
|
||||
case _ =>
|
||||
Left(ConfigError.MissingValue(key.name))
|
||||
|
||||
|
|
|
@ -24,7 +24,8 @@ object ConfigError:
|
|||
* @param candidateValue
|
||||
* The raw value that could not be parsed.
|
||||
* @param source
|
||||
* The [[ConfigSource]] which provided the candidate value.
|
||||
* The name of the [[gs.config.v0.source.ConfigSource]] which provided the
|
||||
* candidate value.
|
||||
*/
|
||||
case class CannotParseValue(
|
||||
configName: ConfigName,
|
||||
|
|
|
@ -37,5 +37,5 @@ object ConfigKey:
|
|||
*/
|
||||
case class WithDefaultValue[A: Configurable](
|
||||
name: ConfigName,
|
||||
defaultValue: A
|
||||
defaultValue: () => A
|
||||
) extends ConfigKey[A]
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package gs.config.v0
|
||||
|
||||
import cats.Eq
|
||||
import cats.Show
|
||||
|
||||
/** Uniquely names some piece of configuration. This structure does _not_
|
||||
* attempt to support every possible use case, but supports some common cases
|
||||
* for users that follow some basic rules. Please review conversion functions
|
||||
|
@ -32,6 +35,13 @@ object ConfigName:
|
|||
|
||||
given CanEqual[ConfigName, ConfigName] = CanEqual.derived
|
||||
|
||||
given Eq[ConfigName] = (
|
||||
x,
|
||||
y
|
||||
) => x == y
|
||||
|
||||
given Show[ConfigName] = _.unwrap()
|
||||
|
||||
extension (name: ConfigName)
|
||||
/** Extract the unmodified string that backs this name.
|
||||
*
|
||||
|
|
|
@ -5,6 +5,15 @@ import cats.effect.Sync
|
|||
import cats.syntax.all.*
|
||||
import gs.config.v0.ConfigName
|
||||
|
||||
/** A `ConfigManifest` tracks all queries for individual pieces of
|
||||
* configuration. It is used in conjunction with an [[AuditedConfiguration]].
|
||||
* Manifests can be queried by producing a _snapshot_ of interactions so far.
|
||||
*
|
||||
* See:
|
||||
*
|
||||
* - [[AuditedConfiguration]]
|
||||
* - [[ConfigQueryResult]]
|
||||
*/
|
||||
trait ConfigManifest[F[_]]:
|
||||
/** Retrieve a snapshot of the current state of this configuration manifest.
|
||||
* This state tracks all configuration names that the caller attempted to
|
||||
|
@ -46,9 +55,13 @@ object ConfigManifest:
|
|||
private val manifest: Ref[F, Map[ConfigName, List[ConfigQueryResult]]]
|
||||
) extends ConfigManifest[F]:
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def snapshot(): F[Map[ConfigName, List[ConfigQueryResult]]] =
|
||||
manifest.get
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def record(
|
||||
name: ConfigName,
|
||||
query: ConfigQueryResult
|
||||
|
|
|
@ -14,7 +14,7 @@ object ConfigQueryResult:
|
|||
/** Represents a query for some configuration that completed successfully.
|
||||
*
|
||||
* @param source
|
||||
* The source which provided the value.
|
||||
* The name of the source which provided the value.
|
||||
* @param rawValue
|
||||
* The raw value that the source returned.
|
||||
*/
|
||||
|
|
|
@ -27,17 +27,32 @@ trait ConfigSource[F[_]]:
|
|||
|
||||
object ConfigSource:
|
||||
|
||||
/** Instantiate a new [[MemoryConfigSource]] based on the given map.
|
||||
*
|
||||
* @param configs
|
||||
* The config map that should back the source.
|
||||
* @return
|
||||
* The new [[ConfigSource]].
|
||||
*/
|
||||
def inMemory[F[_]: Applicative](
|
||||
configs: Map[String, String]
|
||||
): ConfigSource[F] =
|
||||
new MemoryConfigSource[F](configs)
|
||||
|
||||
/** @return
|
||||
* A new [[EnvironmentConfigSource]].
|
||||
*/
|
||||
def environment[F[_]: Sync]: ConfigSource[F] =
|
||||
new EnvironmentConfigSource[F]
|
||||
|
||||
/** @return
|
||||
* An empty [[ConfigSource]].
|
||||
*/
|
||||
def empty[F[_]: Applicative]: ConfigSource[F] =
|
||||
new Empty[F]
|
||||
|
||||
/** Default [[ConfigSource]] implementation that never returns a value.
|
||||
*/
|
||||
final class Empty[F[_]: Applicative] extends ConfigSource[F]:
|
||||
|
||||
/** @inheritDocs
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package gs.config.v0
|
||||
|
||||
import cats.syntax.all.*
|
||||
|
||||
class ConfigNameTests extends munit.FunSuite:
|
||||
|
||||
test("should express some name as an environment variable") {
|
||||
|
@ -14,6 +16,7 @@ class ConfigNameTests extends munit.FunSuite:
|
|||
val expected = raw
|
||||
val name = ConfigName(raw)
|
||||
assertEquals(name.unwrap(), expected)
|
||||
assertEquals(name.show, expected)
|
||||
}
|
||||
|
||||
test("should support equality") {
|
||||
|
|
|
@ -143,7 +143,7 @@ class AuditedConfigurationTests extends GsSuite:
|
|||
|
||||
iotest("should audit the use of default values") {
|
||||
val name = Names.KString
|
||||
val defaultValue = "value"
|
||||
val defaultValue = () => "value"
|
||||
val key = ConfigKey.WithDefaultValue(name, defaultValue)
|
||||
for
|
||||
config <- Configuration
|
||||
|
@ -153,7 +153,7 @@ class AuditedConfigurationTests extends GsSuite:
|
|||
value <- config.getValue(key)
|
||||
manifest <- config.manifest.snapshot()
|
||||
yield
|
||||
assertEquals(value, Right(defaultValue))
|
||||
assertEquals(value, Right(defaultValue()))
|
||||
assertEquals(
|
||||
manifest.get(name),
|
||||
Some(
|
||||
|
|
Loading…
Add table
Reference in a new issue