1/**
2 * Shared utility helpers for the OpenAPI generator.
3 *
4 * This module centralizes the small transformations used while rendering
5 * generated TypeScript, including operation-name normalization, optional
6 * description handling, safe JSDoc comment emission, and direct array merging
7 * for code-generation accumulators.
8 *
9 * @since 4.0.0
10 */
11import * as String from "effect/String"
12import * as UndefinedOr from "effect/UndefinedOr"
13
14/**
15 * Converts an OpenAPI name into the generator's camel-case form.
16 *
17 * **Details**
18 *
19 * Separators are removed, leading digits are ignored, and letters following a
20 * separator or digit are upper-cased without otherwise changing letter casing.
21 *
22 * @category converting
23 * @since 4.0.0
24 */
25export const camelize = (self: string): string => {
26 let str = ""
27 let hadSymbol = false
28 for (let i = 0; i < self.length; i++) {
29 const charCode = self.charCodeAt(i)
30 if (
31 (charCode >= 65 && charCode <= 90) ||
32 (charCode >= 97 && charCode <= 122)
33 ) {
34 str += hadSymbol ? self[i].toUpperCase() : self[i]
35 hadSymbol = false
36 } else if (charCode >= 48 && charCode <= 57) {
37 if (str.length > 0) {
38 str += self[i]
39 hadSymbol = true
40 }
41 } else if (str.length > 0) {
42 hadSymbol = true
43 }
44 }
45 return str
46}
47
48/**
49 * Converts an OpenAPI operation id into the exported operation identifier used
50 * by generated TypeScript modules.
51 *
52 * @category converting
53 * @since 4.0.0
54 */
55export const identifier = (operationId: string) => String.capitalize(camelize(operationId))
56
57/**
58 * Extracts a trimmed, non-empty string from an unknown value.
59 *
60 * **Details**
61 *
62 * Returns `undefined` for non-string values and for strings containing only
63 * whitespace.
64 *
65 * @category filtering
66 * @since 4.0.0
67 */
68export const nonEmptyString = (a: unknown): string | undefined => {
69 if (typeof a === "string") {
70 const trimmed = String.trim(a)
71 if (String.isNonEmpty(trimmed)) {
72 return trimmed
73 }
74 }
75}
76
77/**
78 * Renders an optional description as a JSDoc block for generated TypeScript.
79 *
80 * **Details**
81 *
82 * Returns an empty string when the description is absent and escapes any
83 * closing comment marker so generated source remains syntactically valid.
84 *
85 * @category converting
86 * @since 4.0.0
87 */
88export const toComment = UndefinedOr.match({
89 onUndefined: () => "",
90 onDefined: (description: string) =>
91 `/**
92* ${description.replace(/\*\//g, " * /").split("\n").join("\n* ")}
93*/\n`
94})
95
96/**
97 * Appends every element from `source` into `destination` in order.
98 *
99 * **Details**
100 *
101 * This mutates `destination` directly, which avoids allocating an intermediate
102 * array when generator code needs to merge collections.
103 *
104 * @category mutations
105 * @since 4.0.0
106 */
107export const spreadElementsInto = <A>(source: Array<A>, destination: Array<A>): void => {
108 for (let i = 0; i < source.length; i++) {
109 destination.push(source[i])
110 }
111}