1import type * as Schema from "../../Schema.ts"
2import * as SchemaAST from "../../SchemaAST.ts"
3
4const emptyParamNames: ReadonlySet<string> = new Set()
5
6/**
7 * @internal
8 */
9export function getParamNames(schema: Schema.Constraint | undefined): ReadonlySet<string> | undefined {
10 if (schema === undefined) return emptyParamNames
11 const ast = SchemaAST.getLastEncoding(schema.ast)
12 return SchemaAST.isObjects(ast) && ast.indexSignatures.length === 0
13 ? new Set(ast.propertySignatures.map((ps) => String(ps.name)))
14 : undefined
15}
16
17/**
18 * @internal
19 */
20export function toRouterPath(path: string, schema: Schema.Constraint | undefined): string {
21 // Preserve undeclared params for raw RouteContext consumers.
22 if (schema === undefined || !path.includes(":")) return path
23 const paramNames = getParamNames(schema)
24 if (paramNames === undefined) return path
25
26 let out = ""
27 let depth = 0
28 let regexStart = -1
29 for (let i = 0; i < path.length; i++) {
30 const char = path[i]
31 if (depth > 0 || i === regexStart) {
32 out += char
33 if (char === "\\") out += path[++i] ?? ""
34 else if (char === "(") depth++
35 else if (char === ")") depth--
36 continue
37 }
38 if (char !== ":") {
39 out += char
40 continue
41 }
42 if (path[i + 1] === ":") {
43 out += "::"
44 i++
45 continue
46 }
47
48 const param = /^:(\w+)/.exec(path.slice(i))
49 if (param !== null && paramNames.has(param[1])) {
50 out += param[0]
51 i += param[0].length - 1
52 const next = path[i + 1]
53 if (next === "(") regexStart = i + 1
54 // FindMyWay needs a regex to end the parameter name here.
55 else if (next !== undefined && !"/.-?".includes(next)) out += "(.*?)"
56 } else {
57 out += "::"
58 }
59 }
60 return out
61}