Skip to content

Commit

Permalink
add Config.literal (#1795)
Browse files Browse the repository at this point in the history
  • Loading branch information
matheuspuel authored Jan 1, 2024
1 parent 7b5eaa3 commit 9f2bc5a
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/hot-dryers-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": patch
---

add Config.literal
15 changes: 15 additions & 0 deletions packages/effect/src/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,21 @@ export const number: (name?: string) => Config<number> = internal.number
*/
export const integer: (name?: string) => Config<number> = internal.integer

/**
* Constructs a config for a literal value.
*
* @example
* import { Config } from "effect"
*
* const config = Config.literal("http", "https")("PROTOCOL")
*
* @since 2.0.0
* @category constructors
*/
export const literal: <Literals extends ReadonlyArray<internal.LiteralValue>>(...literals: Literals) => (
name?: string
) => Config<Literals[number]> = internal.literal

/**
* Constructs a config for a `LogLevel` value.
*
Expand Down
24 changes: 24 additions & 0 deletions packages/effect/src/internal/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,30 @@ export const integer = (name?: string): Config.Config<number> => {
return name === undefined ? config : nested(config, name)
}

/** @internal */
export type LiteralValue = string | number | boolean | null | bigint

/** @internal */
export const literal = <Literals extends ReadonlyArray<LiteralValue>>(...literals: Literals) =>
(
name?: string
): Config.Config<Literals[number]> => {
const valuesString = literals.map(String).join(", ")
const config = primitive(`one of (${valuesString})`, (text) => {
const found = literals.find((value) => String(value) === text)
if (found === undefined) {
return Either.left(
configError.InvalidData(
[],
`Expected one of (${valuesString}) but received ${text}`
)
)
}
return Either.right(found)
})
return name === undefined ? config : nested(config, name)
}

/** @internal */
export const logLevel = (name?: string): Config.Config<LogLevel.LogLevel> => {
const config = mapOrFail(string(), (value) => {
Expand Down
29 changes: 29 additions & 0 deletions packages/effect/test/Config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,35 @@ describe("Config", () => {
})
})

describe("literal", () => {
it("name = undefined", () => {
const config = Config.array(Config.literal("a", "b")(), "ITEMS")
assertSuccess(config, [["ITEMS", "a"]], ["a"])
assertFailure(
config,
[["ITEMS", "value"]],
ConfigError.InvalidData(["ITEMS"], "Expected one of (a, b) but received value")
)
})

it("name != undefined", () => {
const config = Config.literal("a", 0, -0.3, BigInt(5), false, null)("LITERAL")
assertSuccess(config, [["LITERAL", "a"]], "a")
assertSuccess(config, [["LITERAL", "0"]], 0)
assertSuccess(config, [["LITERAL", "-0.3"]], -0.3)
assertSuccess(config, [["LITERAL", "5"]], BigInt(5))
assertSuccess(config, [["LITERAL", "false"]], false)
assertSuccess(config, [["LITERAL", "null"]], null)

assertFailure(config, [], ConfigError.MissingData(["LITERAL"], "Expected LITERAL to exist in the provided map"))
assertFailure(
config,
[["LITERAL", "value"]],
ConfigError.InvalidData(["LITERAL"], "Expected one of (a, 0, -0.3, 5, false, null) but received value")
)
})
})

describe("date", () => {
it("name = undefined", () => {
const config = Config.date()
Expand Down

0 comments on commit 9f2bc5a

Please sign in to comment.