Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: default value functions #1139

Merged
merged 11 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions ark/schema/structure/optional.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { printable, throwParseError, unset } from "@ark/util"
import { printable, throwParseError, unset, type Primitive } from "@ark/util"
import type { BaseRoot } from "../roots/root.ts"
import type { declareNode } from "../shared/declare.ts"
import { ArkErrors } from "../shared/errors.ts"
Expand Down Expand Up @@ -95,23 +95,36 @@ export const Optional = {
Node: OptionalNode
}

const isPrimitive = (value: unknown): value is Primitive =>
typeof value === "object" ? value === null : typeof value !== "function"

export const assertDefaultValueAssignability = (
node: BaseRoot,
value: unknown,
key = ""
): unknown => {
const out = node.in(value)
if (!isPrimitive(value) && typeof value !== "function")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just get rid of isPrimitive and instead use hasDomain(value, "object") (it works like the TS keyword so it handles this logic built in, and we use it throughout the repo)

throwParseError(writeNonPrimitiveNonFunctionDefaultValueMessage(key))

const out = node.in(typeof value === "function" ? value() : value)
if (out instanceof ArkErrors)
throwParseError(writeUnassignableDefaultValueMessage(out.message, key))

return value
}

export const writeUnassignableDefaultValueMessage = (
message: string,
key = ""
): string => `Default value${key && ` for key ${key}`} ${message}`
): string =>
`Default value${key && ` for key ${key}`} is not assignable: ${message}`
Dimava marked this conversation as resolved.
Show resolved Hide resolved

export type writeUnassignableDefaultValueMessage<
baseDef extends string,
defaultValue extends string
> = `Default value ${defaultValue} is not assignable to ${baseDef}`

export const writeNonPrimitiveNonFunctionDefaultValueMessage = (
key: string
): string =>
`Default value${key && ` for key ${key}`} is not primitive so it should be specified as a function like () => ({my: 'object'})`
21 changes: 14 additions & 7 deletions ark/schema/structure/prop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,11 @@ export abstract class BaseProp<
return result
}

private premorphedDefaultValue: unknown =
this.hasDefault() ?
this.value.includesMorph ?
this.value.assert(this.default)
: this.default
: undefined
private morphedDefaultFactory: () => unknown = this.getMorphedFactory()

private defaultValueMorphs: Morph[] = [
data => {
data[this.key] = this.premorphedDefaultValue
data[this.key] = this.morphedDefaultFactory()
return data
}
]
Expand All @@ -143,6 +138,18 @@ export abstract class BaseProp<
hasDefault(): this is Optional.Node & { default: unknown } {
return "default" in this
}
getDefaultFactory(): () => unknown {
if (!this.hasDefault()) return () => undefined
if (typeof this.default === "function") return this.default as () => unknown
return () => this.default
}
getMorphedFactory(): () => unknown {
if (!this.hasDefault()) return () => undefined
const factory = this.getDefaultFactory()
return this.value.includesMorph ?
() => this.value.assert(factory())
: factory
}

traverseAllows: TraverseAllows<object> = (data, ctx) => {
if (this.key in data) {
Expand Down
Loading