|
1 |
| -import curry from "just-curry-it"; |
2 |
| - |
3 |
| - |
4 | 1 | export const nil = "";
|
5 | 2 |
|
6 |
| -const EXISTS = Symbol("EXISTS"); |
7 |
| - |
8 |
| -const segmentGenerator = (pointer) => { |
| 3 | +export const pointerSegments = function* (pointer) { |
9 | 4 | if (pointer.length > 0 && pointer[0] !== "/") {
|
10 | 5 | throw Error("Invalid JSON Pointer");
|
11 | 6 | }
|
12 | 7 |
|
13 | 8 | let segmentStart = 1;
|
14 | 9 | let segmentEnd = 0;
|
15 | 10 |
|
16 |
| - return (mode) => { |
17 |
| - if (mode === EXISTS) { |
18 |
| - return segmentEnd < pointer.length; |
19 |
| - } |
20 |
| - |
21 |
| - if (segmentEnd >= pointer.length) { |
22 |
| - return; |
23 |
| - } |
24 |
| - |
| 11 | + while (segmentEnd < pointer.length) { |
25 | 12 | const position = pointer.indexOf("/", segmentStart);
|
26 | 13 | segmentEnd = position === -1 ? pointer.length : position;
|
27 |
| - const segment = unescape(pointer.slice(segmentStart, segmentEnd)); |
| 14 | + const segment = pointer.slice(segmentStart, segmentEnd); |
28 | 15 | segmentStart = segmentEnd + 1;
|
29 | 16 |
|
30 |
| - return segment; |
31 |
| - }; |
| 17 | + yield unescape(segment); |
| 18 | + } |
32 | 19 | };
|
33 | 20 |
|
34 | 21 | export const get = (pointer, subject = undefined) => {
|
35 |
| - const nextSegment = segmentGenerator(pointer); |
36 |
| - const fn = (subject) => _get(nextSegment, subject, nil); |
37 |
| - return subject === undefined ? fn : fn(subject); |
| 22 | + if (subject === undefined) { |
| 23 | + const segments = [...pointerSegments(pointer)]; |
| 24 | + return (subject) => _get(segments, subject); |
| 25 | + } else { |
| 26 | + return _get(pointerSegments(pointer), subject); |
| 27 | + } |
38 | 28 | };
|
39 | 29 |
|
40 |
| -const _get = (nextSegment, subject, cursor) => { |
41 |
| - if (!nextSegment(EXISTS)) { |
42 |
| - return subject; |
43 |
| - } else { |
44 |
| - const segment = nextSegment(); |
45 |
| - return _get(nextSegment, applySegment(subject, segment, cursor), append(segment, cursor)); |
| 30 | +const _get = (segments, subject) => { |
| 31 | + let cursor = nil; |
| 32 | + for (const segment of segments) { |
| 33 | + subject = applySegment(subject, segment, cursor); |
| 34 | + cursor = append(segment, cursor); |
46 | 35 | }
|
| 36 | + |
| 37 | + return subject; |
47 | 38 | };
|
48 | 39 |
|
49 | 40 | export const set = (pointer, subject = undefined, value = undefined) => {
|
50 |
| - const nextSegment = segmentGenerator(pointer); |
51 |
| - const fn = curry((subject, value) => _set(nextSegment, subject, value, nil)); |
52 |
| - return subject === undefined ? fn : fn(subject, value); |
| 41 | + if (subject === undefined) { |
| 42 | + const segments = [...pointerSegments(pointer)]; |
| 43 | + return (subject, value) => _set(segments.values(), subject, value); |
| 44 | + } else { |
| 45 | + return _set(pointerSegments(pointer), subject, value); |
| 46 | + } |
53 | 47 | };
|
54 | 48 |
|
55 |
| -const _set = (nextSegment, subject, value, cursor) => { |
56 |
| - const segment = nextSegment(); |
57 |
| - if (segment === undefined) { |
| 49 | +const _set = (segments, subject, value, cursor = nil) => { |
| 50 | + const segment = segments.next(); |
| 51 | + if (segment.done) { |
58 | 52 | return value;
|
59 |
| - } else if (nextSegment(EXISTS)) { |
60 |
| - if (Array.isArray(subject)) { |
61 |
| - const clonedSubject = [...subject]; |
62 |
| - clonedSubject[segment] = _set(nextSegment, applySegment(subject, segment, cursor), value, append(segment, cursor)); |
63 |
| - return clonedSubject; |
64 |
| - } else { |
65 |
| - return { ...subject, [segment]: _set(nextSegment, applySegment(subject, segment, cursor), value, append(segment, cursor)) }; |
66 |
| - } |
67 |
| - } else if (Array.isArray(subject)) { |
68 |
| - const clonedSubject = [...subject]; |
69 |
| - clonedSubject[computeSegment(subject, segment)] = value; |
70 |
| - return clonedSubject; |
| 53 | + } |
| 54 | + |
| 55 | + if (Array.isArray(subject)) { |
| 56 | + subject = [...subject]; |
71 | 57 | } else if (typeof subject === "object" && subject !== null) {
|
72 |
| - return { ...subject, [segment]: value }; |
| 58 | + subject = { ...subject }; |
73 | 59 | } else {
|
74 |
| - return applySegment(subject, segment, cursor); |
| 60 | + applySegment(subject, segment.value, cursor); |
75 | 61 | }
|
| 62 | + cursor = append(segment.value, cursor); |
| 63 | + |
| 64 | + const computedSegment = computeSegment(subject, segment.value); |
| 65 | + subject[computedSegment] = _set(segments, subject[computedSegment], value, cursor); |
| 66 | + return subject; |
76 | 67 | };
|
77 | 68 |
|
78 | 69 | export const assign = (pointer, subject = undefined, value = undefined) => {
|
79 |
| - const nextSegment = segmentGenerator(pointer); |
80 |
| - const fn = curry((subject, value) => _assign(nextSegment, subject, value, nil)); |
81 |
| - return subject === undefined ? fn : fn(subject, value); |
| 70 | + if (subject === undefined) { |
| 71 | + const segments = [...pointerSegments(pointer)]; |
| 72 | + return (subject, value) => _assign(segments.values(), subject, value); |
| 73 | + } else { |
| 74 | + return _assign(pointerSegments(pointer), subject, value); |
| 75 | + } |
82 | 76 | };
|
83 | 77 |
|
84 |
| -const _assign = (nextSegment, subject, value, cursor) => { |
85 |
| - const segment = nextSegment(); |
86 |
| - if (segment === undefined) { |
87 |
| - return; |
88 |
| - } else if (!nextSegment(EXISTS) && !isScalar(subject)) { |
89 |
| - subject[computeSegment(subject, segment)] = value; |
90 |
| - } else { |
91 |
| - _assign(nextSegment, applySegment(subject, segment, cursor), value, append(segment, cursor)); |
| 78 | +const _assign = (segments, subject, value, cursor = nil) => { |
| 79 | + let lastSegment; |
| 80 | + let lastSubject; |
| 81 | + for (let segment of segments) { |
| 82 | + segment = computeSegment(subject, segment); |
| 83 | + lastSegment = segment; |
| 84 | + lastSubject = subject; |
| 85 | + subject = applySegment(subject, segment, cursor); |
| 86 | + cursor = append(segment, cursor); |
| 87 | + } |
| 88 | + |
| 89 | + if (lastSubject !== undefined) { |
| 90 | + lastSubject[lastSegment] = value; |
92 | 91 | }
|
93 | 92 | };
|
94 | 93 |
|
95 | 94 | export const unset = (pointer, subject = undefined) => {
|
96 |
| - const nextSegment = segmentGenerator(pointer); |
97 |
| - const fn = (subject) => _unset(nextSegment, subject, nil); |
98 |
| - return subject === undefined ? fn : fn(subject); |
| 95 | + if (subject === undefined) { |
| 96 | + const segments = [...pointerSegments(pointer)]; |
| 97 | + return (subject) => _unset(segments.values(), subject); |
| 98 | + } else { |
| 99 | + return _unset(pointerSegments(pointer), subject); |
| 100 | + } |
99 | 101 | };
|
100 | 102 |
|
101 |
| -const _unset = (nextSegment, subject, cursor) => { |
102 |
| - const segment = nextSegment(); |
103 |
| - if (segment === undefined) { |
| 103 | +const _unset = (segments, subject, cursor = nil) => { |
| 104 | + const segment = segments.next(); |
| 105 | + if (segment.done) { |
104 | 106 | return;
|
105 |
| - } else if (nextSegment(EXISTS)) { |
106 |
| - const value = applySegment(subject, segment, cursor); |
107 |
| - return { ...subject, [segment]: _unset(nextSegment, value, append(segment, cursor)) }; |
108 |
| - } else if (Array.isArray(subject)) { |
109 |
| - const clonedSubject = [...subject]; |
110 |
| - delete clonedSubject[computeSegment(subject, segment)]; |
111 |
| - return clonedSubject; |
| 107 | + } |
| 108 | + |
| 109 | + if (Array.isArray(subject)) { |
| 110 | + subject = [...subject]; |
112 | 111 | } else if (typeof subject === "object" && subject !== null) {
|
113 |
| - // eslint-disable-next-line no-unused-vars |
114 |
| - const { [segment]: _, ...result } = subject; |
115 |
| - return result; |
| 112 | + subject = { ...subject }; |
116 | 113 | } else {
|
117 |
| - return applySegment(subject, segment, cursor); |
| 114 | + applySegment(subject, segment.value, cursor); |
118 | 115 | }
|
| 116 | + cursor = append(segment.value, cursor); |
| 117 | + |
| 118 | + const computedSegment = computeSegment(subject, segment.value); |
| 119 | + const unsetSubject = _unset(segments, subject[computedSegment], cursor); |
| 120 | + if (computedSegment in subject) { |
| 121 | + subject[computedSegment] = unsetSubject; |
| 122 | + } |
| 123 | + return subject; |
119 | 124 | };
|
120 | 125 |
|
121 | 126 | export const remove = (pointer, subject = undefined) => {
|
122 |
| - const nextSegment = segmentGenerator(pointer); |
123 |
| - const fn = (subject) => _remove(nextSegment, subject, nil); |
124 |
| - return subject === undefined ? fn : fn(subject); |
| 127 | + if (subject === undefined) { |
| 128 | + const segments = [...pointerSegments(pointer)]; |
| 129 | + return (subject) => _remove(segments.values(), subject); |
| 130 | + } else { |
| 131 | + return _remove(pointerSegments(pointer), subject); |
| 132 | + } |
125 | 133 | };
|
126 | 134 |
|
127 |
| -const _remove = (nextSegment, subject, cursor) => { |
128 |
| - const segment = nextSegment(); |
129 |
| - if (segment === undefined) { |
130 |
| - return; |
131 |
| - } else if (nextSegment(EXISTS)) { |
132 |
| - const value = applySegment(subject, segment, cursor); |
133 |
| - _remove(nextSegment, value, append(segment, cursor)); |
134 |
| - } else if (!isScalar(subject)) { |
135 |
| - delete subject[segment]; |
136 |
| - } else { |
137 |
| - applySegment(subject, segment, cursor); |
| 135 | +const _remove = (segments, subject, cursor = nil) => { |
| 136 | + let lastSegment; |
| 137 | + let lastSubject; |
| 138 | + for (let segment of segments) { |
| 139 | + segment = computeSegment(subject, segment); |
| 140 | + lastSegment = segment; |
| 141 | + lastSubject = subject; |
| 142 | + subject = applySegment(subject, segment, cursor); |
| 143 | + cursor = append(segment, cursor); |
| 144 | + } |
| 145 | + |
| 146 | + if (lastSubject !== undefined) { |
| 147 | + delete lastSubject[lastSegment]; |
138 | 148 | }
|
139 | 149 | };
|
140 | 150 |
|
141 |
| -export const append = curry((segment, pointer) => pointer + "/" + escape(segment)); |
| 151 | +export const append = (segment, pointer) => pointer + "/" + escape(segment); |
142 | 152 |
|
143 | 153 | const escape = (segment) => segment.toString().replace(/~/g, "~0").replace(/\//g, "~1");
|
144 | 154 | const unescape = (segment) => segment.toString().replace(/~1/g, "/").replace(/~0/g, "~");
|
|
0 commit comments