Skip to content

Commit

Permalink
Merge pull request #1173 from samchon/features/notations
Browse files Browse the repository at this point in the history
Fix #1172: enhance notations' types and functions.
  • Loading branch information
samchon authored Jul 20, 2024
2 parents 6bea5bb + 5fee358 commit b8f796e
Show file tree
Hide file tree
Showing 88 changed files with 166 additions and 934 deletions.
2 changes: 1 addition & 1 deletion benchmark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,6 @@
"suppress-warnings": "^1.0.2",
"tstl": "^3.0.0",
"uuid": "^9.0.1",
"typia": "../typia-6.5.2.tgz"
"typia": "../typia-6.5.3.tgz"
}
}
43 changes: 43 additions & 0 deletions debug/features/camel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import typia from "typia";

interface Something {
something: string;
SOMETHING: string;

somethingMusic: string;
SomethingMusic: string;
something_music: string;
SOMETHING_MUSIC: string;

x__y__z__and__something: string;
}
interface SomethingOfCamel {
something: string;
somethingMusic: string;
xYZAndSomething: string;
}
interface SomethingOfPascal {
Something: string;
SOMETHING: string;
SomethingMusic: string;
XYZAndSomething: string;
}
interface SomethingOfSnake {
something: string;
something_music: string;
x__y__z__and__something: string;
}

const something: Something = typia.random<Something>();
const x = typia.notations.camel(something);
const y = typia.notations.pascal(something);
const z = typia.notations.snake(something);

console.log(x);
typia.assertEquals<SomethingOfCamel>(x);

console.log(y);
typia.assertEquals<SomethingOfPascal>(y);

console.log(z);
typia.assertEquals<SomethingOfSnake>(z);
2 changes: 1 addition & 1 deletion debug/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
"typescript": "^5.4.2"
},
"dependencies": {
"typia": "../typia-6.5.0.tgz"
"typia": "../typia-6.5.3-dev.20240720.tgz"
}
}
2 changes: 1 addition & 1 deletion errors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@
"typescript": "^5.3.2"
},
"dependencies": {
"typia": "../typia-6.5.2.tgz"
"typia": "../typia-6.5.3.tgz"
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typia",
"version": "6.5.2",
"version": "6.5.3",
"description": "Superfast runtime validators with only one line",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
4 changes: 2 additions & 2 deletions packages/typescript-json/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typescript-json",
"version": "6.5.2",
"version": "6.5.3",
"description": "Superfast runtime validators with only one line",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -63,7 +63,7 @@
},
"homepage": "https://typia.io",
"dependencies": {
"typia": "6.5.2"
"typia": "6.5.3"
},
"peerDependencies": {
"typescript": ">=4.8.0 <5.6.0"
Expand Down
21 changes: 14 additions & 7 deletions src/CamelCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,17 @@ type CamelizeTuple<T extends readonly any[]> = T extends []

type CamelizeString<Key extends string> = Key extends `_${infer R}`
? `_${CamelizeString<R>}`
: Key extends `${infer F}${infer R}`
? `${Lowercase<F>}${CamelizeStringRepeatedly<R>}`
: Key;
type CamelizeStringRepeatedly<Key extends string> =
Key extends `${infer F}_${infer R}`
? `${F}${Capitalize<CamelizeStringRepeatedly<R>>}`
: Key;
: Key extends `${infer _F}_${infer _R}`
? CamelizeSnakeString<Key>
: Key extends Uppercase<Key>
? Lowercase<Key>
: CamelizePascalString<Key>;
type CamelizePascalString<Key extends string> =
Key extends `${infer F}${infer R}` ? `${Lowercase<F>}${R}` : Key;
type CamelizeSnakeString<Key extends string> = Key extends `_${infer R}`
? CamelizeSnakeString<R>
: Key extends `${infer F}_${infer M}${infer R}`
? M extends "_"
? CamelizeSnakeString<`${F}_${R}`>
: `${Lowercase<F>}${Uppercase<M>}${CamelizeSnakeString<R>}`
: Lowercase<Key>;
17 changes: 10 additions & 7 deletions src/PascalCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,13 @@ type PascalizeTuple<T extends readonly any[]> = T extends []

type PascalizeString<Key extends string> = Key extends `_${infer R}`
? `_${PascalizeString<R>}`
: Key extends `${infer F}${infer R}`
? `${Uppercase<F>}${PascalizeStringRepeatedly<R>}`
: Key;
type PascalizeStringRepeatedly<Key extends string> =
Key extends `${infer F}_${infer R}`
? `${F}${Capitalize<PascalizeStringRepeatedly<R>>}`
: Key;
: Key extends `${infer _F}_${infer _R}`
? PascalizeSnakeString<Key>
: Capitalize<Key>;
type PascalizeSnakeString<Key extends string> = Key extends `_${infer R}`
? PascalizeSnakeString<R>
: Key extends `${infer F}${infer M}_${infer R}`
? `${Uppercase<F>}${Lowercase<M>}${PascalizeSnakeString<R>}`
: Key extends `${infer F}${infer R}`
? `${Uppercase<F>}${Lowercase<R>}`
: Key;
89 changes: 46 additions & 43 deletions src/utils/NamingConvention/NamingConvention.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
import { StringUtil } from "../StringUtil";

export function snake(str: string): string {
if (str.length === 0) return str;

// PREFIX
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let prefix: string = "";
for (let i: number = 0; i < str.length; i++) {
if (str[i] === "_") prefix += "_";
else break;
}
if (prefix.length !== 0) str = str.substring(prefix.length);

const out = (s: string) => `${prefix}${s}`;

// SNAKE CASE
const items: string[] = str.split("_");
if (items.length > 1) return out(items.map((s) => s.toLowerCase()).join("_"));

// CAMEL OR PASCAL CASE
const indexes: number[] = [];
for (let i: number = 0; i < str.length; i++) {
const code: number = str.charCodeAt(i);
Expand All @@ -23,29 +41,33 @@ export function snake(str: string): string {
ret += "_";
}
ret += str.substring(indexes[indexes.length - 1]!).toLowerCase();
return ret;
return out(ret);
}

export function camel(str: string): string {
return unsnake((str: string) => {
if (str.length === 0) return str;
else if (str[0] === str[0]!.toUpperCase())
return str[0]!.toLowerCase() + str.substring(1);
else return str;
export const camel = (str: string): string =>
unsnake({
plain: (str) =>
str.length
? str === str.toUpperCase()
? str.toLocaleLowerCase()
: `${str[0]!.toLowerCase()}${str.substring(1)}`
: str,
snake: (str, i) =>
i === 0 ? str.toLowerCase() : StringUtil.capitalize(str.toLowerCase()),
})(str);
}

export function pascal(str: string): string {
return unsnake((str: string) => {
if (str.length === 0) return str;
else if (str[0] === str[0]!.toLowerCase())
return str[0]!.toUpperCase() + str.substring(1);
else return str;
export const pascal = (str: string): string =>
unsnake({
plain: (str) =>
str.length ? `${str[0]!.toUpperCase()}${str.substring(1)}` : str,
snake: StringUtil.capitalize,
})(str);
}

const unsnake =
(escaper: (str: string) => string) =>
(props: {
plain: (str: string) => string;
snake: (str: string, index: number) => string;
}) =>
(str: string): string => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let prefix: string = "";
Expand All @@ -55,32 +77,13 @@ const unsnake =
}
if (prefix.length !== 0) str = str.substring(prefix.length);

const indexes: [number, number][] = [];
for (let i: number = 0; i < str.length; i++) {
const ch: string = str[i]!;
if (ch !== "_") continue;

const last = indexes[indexes.length - 1];
if (last === undefined || last[0] + last[1] !== i) indexes.push([i, 1]);
else ++last[1];
}
if (indexes.length === 0) return prefix + escaper(str);
const out = (s: string) => `${prefix}${s}`;
if (str.length === 0) return out("");

// eslint-disable-next-line @typescript-eslint/no-unused-vars
let ret: string = "";
for (let i: number = 0; i < indexes.length; i++) {
const [first] = indexes[i]!;
if (i === 0)
if (first === 0) ret += "_";
else ret += str.substring(0, first);
else {
const [prevFirst, prevLength] = indexes[i - 1]!;
const piece: string = str.substring(prevFirst + prevLength, first);
if (piece.length) ret += StringUtil.capitalize(piece);
}
}
const last = indexes[indexes.length - 1]!;
const piece: string = str.substring(last[0] + last[1]);
if (last.length) ret += StringUtil.capitalize(piece);
return prefix + escaper(ret);
const items: string[] = str.split("_").filter((s) => s.length !== 0);
return items.length === 0
? out("")
: items.length === 1
? out(props.plain(items[0]!))
: out(items.map(props.snake).join(""));
};
2 changes: 1 addition & 1 deletion src/utils/StringUtil/StringUtil.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const capitalize = (str: string) =>
str.length ? str[0]!.toUpperCase() + str.slice(1) : str;
str.length ? str[0]!.toUpperCase() + str.slice(1).toLowerCase() : str;

export const escapeDuplicate =
(keep: string[]) =>
Expand Down
2 changes: 1 addition & 1 deletion test-esm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@
"typescript": "^5.4.5"
},
"dependencies": {
"typia": "../typia-6.5.2.tgz"
"typia": "../typia-6.5.3.tgz"
}
}
2 changes: 2 additions & 0 deletions test/build/internal/TestFeature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface TestFeature {
random?: true;
strict?: true;
explicit?: true;
dynamic?: false;
programmer?: (create: boolean) => (structure: string) => string;
}
export namespace TestFeature {
Expand Down Expand Up @@ -400,6 +401,7 @@ export namespace TestFeature {
creatable: true,
resolved: true as const,
spoilable: false,
dynamic: false as const,
programmer: write_notation({
method,
mode,
Expand Down
1 change: 1 addition & 0 deletions test/build/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ async function generate(
else if (feat.random && s.RANDOM === false) continue;
else if (feat.method.toLowerCase().includes("prune") && s.ADDABLE === false)
continue;
else if (feat.dynamic === false && s.name.startsWith("Dynamic")) continue;

const location: string = `${path}/test_${
feat.module ? `${feat.module}_` : ""
Expand Down
2 changes: 1 addition & 1 deletion test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@
"suppress-warnings": "^1.0.2",
"tstl": "^3.0.0",
"uuid": "^9.0.1",
"typia": "../typia-6.5.2.tgz"
"typia": "../typia-6.5.3.tgz"
}
}
40 changes: 40 additions & 0 deletions test/src/features/issues/test_issue_1172_upper_cased_snake_case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import typia, { CamelCase, PascalCase, SnakeCase } from "typia";

export const test_issue_1172_upper_cased_snake_case = (): void => {
const something: Something = typia.random<Something>();
const x: CamelCase<Something> = typia.notations.camel(something);
const y: PascalCase<Something> = typia.notations.pascal(something);
const z: SnakeCase<Something> = typia.notations.snake(something);

typia.assertEquals<SomethingOfCamel>(x);
typia.assertEquals<SomethingOfPascal>(y);
typia.assertEquals<SomethingOfSnake>(z);
};

interface Something {
something: string;
SOMETHING: string;

somethingMusic: string;
SomethingMusic: string;
something_music: string;
SOMETHING_MUSIC: string;

x__y__z__and__something: string;
}
interface SomethingOfCamel {
something: string;
somethingMusic: string;
xYZAndSomething: string;
}
interface SomethingOfPascal {
Something: string;
SOMETHING: string;
SomethingMusic: string;
XYZAndSomething: string;
}
interface SomethingOfSnake {
something: string;
something_music: string;
x__y__z__and__something: string;
}

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit b8f796e

Please sign in to comment.