|
1 | 1 | import { RvPath, StoredRhineVar } from 'rhine-var' |
2 | 2 |
|
3 | | -export function makeRvPath(path: string): RvPath { |
4 | | - const rvPath: RvPath = [] |
5 | | - for (const item of path.split('.')) { |
6 | | - const num = parseInt(item, 10) |
7 | | - if (!isNaN(num) && item === num.toString()) { |
8 | | - rvPath.push(num) |
9 | | - } else { |
10 | | - rvPath.push(item) |
| 3 | +/** |
| 4 | + * RvPath 路径工具类 |
| 5 | + * 提供 RvPath 的创建、转换、访问和比较等功能 |
| 6 | + */ |
| 7 | +// eslint-disable-next-line @typescript-eslint/no-extraneous-class |
| 8 | +export class RvUtils { |
| 9 | + /** |
| 10 | + * 将字符串路径转换为 RvPath 数组 |
| 11 | + * @param path - 以点分隔的路径字符串,如 "a.0.b" |
| 12 | + * @returns RvPath 数组,数字字符串会被转换为数字类型 |
| 13 | + * @example |
| 14 | + * RvUtils.makeRvPath("a.0.b") // ['a', 0, 'b'] |
| 15 | + */ |
| 16 | + static makeRvPath(path: string): RvPath { |
| 17 | + const rvPath: RvPath = [] |
| 18 | + for (const item of path.split('.')) { |
| 19 | + const num = parseInt(item, 10) |
| 20 | + if (!isNaN(num) && item === num.toString()) { |
| 21 | + rvPath.push(num) |
| 22 | + } else { |
| 23 | + rvPath.push(item) |
| 24 | + } |
11 | 25 | } |
| 26 | + return rvPath |
12 | 27 | } |
13 | | - return rvPath |
14 | | -} |
15 | | - |
16 | | -export function makeRvPathString(path: RvPath): string { |
17 | | - return path.join('.') |
18 | | -} |
19 | 28 |
|
20 | | -export function ensureRvPath(path: string | RvPath): RvPath { |
21 | | - if (typeof path === 'string') { |
22 | | - return makeRvPath(path) |
| 29 | + /** |
| 30 | + * 将 RvPath 数组转换为字符串路径 |
| 31 | + * @param path - RvPath 数组 |
| 32 | + * @returns 以点分隔的路径字符串 |
| 33 | + * @example |
| 34 | + * RvUtils.makeRvPathString(['a', 0, 'b']) // "a.0.b" |
| 35 | + */ |
| 36 | + static makeRvPathString(path: RvPath): string { |
| 37 | + return path.join('.') |
23 | 38 | } |
24 | | - return path |
25 | | -} |
26 | 39 |
|
27 | | -export function ensureRvPathString(path: string | RvPath): string { |
28 | | - if (typeof path !== 'string') { |
29 | | - return makeRvPathString(path) |
| 40 | + /** |
| 41 | + * 确保路径为 RvPath 数组格式 |
| 42 | + * @param path - 字符串路径或 RvPath 数组 |
| 43 | + * @returns RvPath 数组 |
| 44 | + */ |
| 45 | + static ensureRvPath(path: string | RvPath): RvPath { |
| 46 | + if (typeof path === 'string') { |
| 47 | + return RvUtils.makeRvPath(path) |
| 48 | + } |
| 49 | + return path |
30 | 50 | } |
31 | | - return path |
32 | | -} |
33 | 51 |
|
34 | | -export function getByRvPath( |
35 | | - source: StoredRhineVar | Record<string | number, unknown>, |
36 | | - path: string | RvPath, |
37 | | -): unknown { |
38 | | - const resolvedPath = ensureRvPath(path) |
39 | | - let obj: unknown = source |
40 | | - for (const key of resolvedPath) { |
41 | | - if (obj === undefined || obj === null) { |
42 | | - return undefined |
| 52 | + /** |
| 53 | + * 确保路径为字符串格式 |
| 54 | + * @param path - 字符串路径或 RvPath 数组 |
| 55 | + * @returns 字符串路径 |
| 56 | + */ |
| 57 | + static ensureRvPathString(path: string | RvPath): string { |
| 58 | + if (typeof path !== 'string') { |
| 59 | + return RvUtils.makeRvPathString(path) |
43 | 60 | } |
44 | | - obj = (obj as Record<string | number, unknown>)[key] |
| 61 | + return path |
45 | 62 | } |
46 | | - return obj |
47 | | -} |
48 | 63 |
|
49 | | -export function setByRvPath(source: StoredRhineVar, path: string | RvPath, value: unknown): void { |
50 | | - const resolvedPath = ensureRvPath(path) |
51 | | - let obj: Record<string | number, unknown> = source as Record<string | number, unknown> |
52 | | - for (let i = 0; i < resolvedPath.length - 1; i++) { |
53 | | - obj = obj[resolvedPath[i]] as Record<string | number, unknown> |
| 64 | + /** |
| 65 | + * 根据路径从对象中获取值 |
| 66 | + * @typeParam T - 返回值的类型 |
| 67 | + * @param source - 源对象 |
| 68 | + * @param path - 访问路径 |
| 69 | + * @returns 路径对应的值,如果路径不存在则返回 undefined |
| 70 | + * @example |
| 71 | + * RvUtils.getByRvPath({ a: { b: 1 } }, "a.b") // 1 |
| 72 | + */ |
| 73 | + static getByRvPath<T = unknown>( |
| 74 | + source: StoredRhineVar | Record<string | number, unknown>, |
| 75 | + path: string | RvPath, |
| 76 | + ): T | undefined { |
| 77 | + const resolvedPath = RvUtils.ensureRvPath(path) |
| 78 | + let obj: unknown = source |
| 79 | + for (const key of resolvedPath) { |
| 80 | + if (obj === undefined || obj === null) { |
| 81 | + return undefined |
| 82 | + } |
| 83 | + obj = (obj as Record<string | number, unknown>)[key] |
| 84 | + } |
| 85 | + return obj as T |
54 | 86 | } |
55 | | - obj[resolvedPath[resolvedPath.length - 1]] = value |
56 | | -} |
57 | 87 |
|
58 | | -// 检查两段路径是否完全重叠,长度可不同,开头相同。指从开头开始每一项比较,直到有一方结束,是否全部相同 |
59 | | -export function checkRvPathOverlay(from: RvPath | string, target: RvPath | string): boolean { |
60 | | - const fromPath = ensureRvPath(from) |
61 | | - const targetPath = ensureRvPath(target) |
62 | | - const n = fromPath.length > targetPath.length ? targetPath.length : fromPath.length |
63 | | - for (let i = 0; i < n; i++) { |
64 | | - if (fromPath[i] != targetPath[i]) { |
65 | | - return false |
| 88 | + /** |
| 89 | + * 根据路径设置对象中的值 |
| 90 | + * @param source - 源对象 |
| 91 | + * @param path - 设置路径 |
| 92 | + * @param value - 要设置的值 |
| 93 | + * @example |
| 94 | + * const obj = { a: { b: 1 } } |
| 95 | + * RvUtils.setByRvPath(obj, "a.b", 2) // obj.a.b === 2 |
| 96 | + */ |
| 97 | + static setByRvPath(source: StoredRhineVar, path: string | RvPath, value: unknown): void { |
| 98 | + const resolvedPath = RvUtils.ensureRvPath(path) |
| 99 | + let obj: Record<string | number, unknown> = source as Record<string | number, unknown> |
| 100 | + for (let i = 0; i < resolvedPath.length - 1; i++) { |
| 101 | + obj = obj[resolvedPath[i]] as Record<string | number, unknown> |
66 | 102 | } |
| 103 | + obj[resolvedPath[resolvedPath.length - 1]] = value |
67 | 104 | } |
68 | | - return true |
69 | | -} |
70 | 105 |
|
71 | | -export function checkAnyRvPathOverlay( |
72 | | - from: RvPath | string, |
73 | | - targetList: (RvPath | string)[], |
74 | | -): boolean { |
75 | | - const fromPath = ensureRvPath(from) |
76 | | - return targetList.some((target) => checkRvPathOverlay(fromPath, target)) |
77 | | -} |
| 106 | + /** |
| 107 | + * 检查两段路径是否完全重叠 |
| 108 | + * 长度可不同,从开头开始每一项比较,直到有一方结束,是否全部相同 |
| 109 | + * @param from - 第一个路径 |
| 110 | + * @param target - 第二个路径 |
| 111 | + * @returns 如果路径重叠则返回 true |
| 112 | + * @example |
| 113 | + * RvUtils.checkRvPathOverlay("a.b", "a.b.c") // true |
| 114 | + * RvUtils.checkRvPathOverlay("a.b", "a.c") // false |
| 115 | + */ |
| 116 | + static checkRvPathOverlay(from: RvPath | string, target: RvPath | string): boolean { |
| 117 | + const fromPath = RvUtils.ensureRvPath(from) |
| 118 | + const targetPath = RvUtils.ensureRvPath(target) |
| 119 | + const n = fromPath.length > targetPath.length ? targetPath.length : fromPath.length |
| 120 | + for (let i = 0; i < n; i++) { |
| 121 | + if (fromPath[i] != targetPath[i]) { |
| 122 | + return false |
| 123 | + } |
| 124 | + } |
| 125 | + return true |
| 126 | + } |
78 | 127 |
|
79 | | -// 检查两段路径是否完全相同 |
80 | | -export function checkRvPathSame(from: RvPath | string, target: RvPath | string): boolean { |
81 | | - const fromPath = ensureRvPath(from) |
82 | | - const targetPath = ensureRvPath(target) |
83 | | - if (fromPath.length !== targetPath.length) { |
84 | | - return false |
| 128 | + /** |
| 129 | + * 检查路径是否与目标列表中的任意路径重叠 |
| 130 | + * @param from - 要检查的路径 |
| 131 | + * @param targetList - 目标路径列表 |
| 132 | + * @returns 如果与任意目标路径重叠则返回 true |
| 133 | + */ |
| 134 | + static checkAnyRvPathOverlay(from: RvPath | string, targetList: (RvPath | string)[]): boolean { |
| 135 | + const fromPath = RvUtils.ensureRvPath(from) |
| 136 | + return targetList.some((target) => RvUtils.checkRvPathOverlay(fromPath, target)) |
85 | 137 | } |
86 | | - for (let i = 0; i < fromPath.length; i++) { |
87 | | - if (fromPath[i] !== targetPath[i]) { |
| 138 | + |
| 139 | + /** |
| 140 | + * 检查两段路径是否完全相同 |
| 141 | + * @param from - 第一个路径 |
| 142 | + * @param target - 第二个路径 |
| 143 | + * @returns 如果路径完全相同则返回 true |
| 144 | + * @example |
| 145 | + * RvUtils.checkRvPathSame("a.b", "a.b") // true |
| 146 | + * RvUtils.checkRvPathSame("a.b", "a.b.c") // false |
| 147 | + */ |
| 148 | + static checkRvPathSame(from: RvPath | string, target: RvPath | string): boolean { |
| 149 | + const fromPath = RvUtils.ensureRvPath(from) |
| 150 | + const targetPath = RvUtils.ensureRvPath(target) |
| 151 | + if (fromPath.length !== targetPath.length) { |
88 | 152 | return false |
89 | 153 | } |
| 154 | + for (let i = 0; i < fromPath.length; i++) { |
| 155 | + if (fromPath[i] !== targetPath[i]) { |
| 156 | + return false |
| 157 | + } |
| 158 | + } |
| 159 | + return true |
90 | 160 | } |
91 | | - return true |
92 | | -} |
93 | 161 |
|
94 | | -export function checkAnyRvPathSame( |
95 | | - from: RvPath | string, |
96 | | - targetList: (RvPath | string)[], |
97 | | -): boolean { |
98 | | - const fromPath = ensureRvPath(from) |
99 | | - return targetList.some((target) => checkRvPathSame(fromPath, target)) |
100 | | -} |
101 | | - |
102 | | -// Create a namespace object for backward compatibility |
103 | | -const RvUtils = { |
104 | | - makeRvPath, |
105 | | - makeRvPathString, |
106 | | - ensureRvPath, |
107 | | - ensureRvPathString, |
108 | | - getByRvPath, |
109 | | - setByRvPath, |
110 | | - checkRvPathSame, |
111 | | - checkAnyRvPathSame, |
112 | | - checkRvPathOverlay, |
113 | | - checkAnyRvPathOverlay, |
| 162 | + /** |
| 163 | + * 检查路径是否与目标列表中的任意路径完全相同 |
| 164 | + * @param from - 要检查的路径 |
| 165 | + * @param targetList - 目标路径列表 |
| 166 | + * @returns 如果与任意目标路径相同则返回 true |
| 167 | + */ |
| 168 | + static checkAnyRvPathSame(from: RvPath | string, targetList: (RvPath | string)[]): boolean { |
| 169 | + const fromPath = RvUtils.ensureRvPath(from) |
| 170 | + return targetList.some((target) => RvUtils.checkRvPathSame(fromPath, target)) |
| 171 | + } |
114 | 172 | } |
115 | 173 |
|
116 | 174 | export default RvUtils |
0 commit comments