-
-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathJSObject.swift
More file actions
93 lines (91 loc) · 3.35 KB
/
JSObject.swift
File metadata and controls
93 lines (91 loc) · 3.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import JSON
public final class JSObject {
@usableFromInline var object: [String: JSValue]
@usableFromInline var buffer: [JSValue]
public let isArray: Bool
@inlinable init(object: [String: JSValue], buffer: [JSValue], isArray: Bool) {
self.object = object
self.buffer = buffer
self.isArray = isArray
}
}
extension JSObject {
@inlinable public static func object(_ properties: [String: JSValue] = [:]) -> JSObject {
return JSObject(object: properties, buffer: [], isArray: false)
}
@inlinable public static func array(_ elements: [JSValue] = []) -> JSObject {
return JSObject(object: [:], buffer: elements, isArray: true)
}
}
extension JSObject {
public static func json(_ json: JSON.Object) throws -> JSObject {
var object: [String: JSValue] = .init(minimumCapacity: json.fields.count)
for (key, value): (JSON.Key, JSON.Node) in json.fields {
if case _? = object.updateValue(try .json(value), forKey: key.rawValue) {
throw JSON.ObjectKeyError<JSON.Key>.duplicate(key)
}
}
return .object(object)
}
public static func json(_ json: JSON.Array) throws -> JSObject {
return .array(try json.elements.map(JSValue.json(_:)))
}
}
extension JSObject: ConvertibleToJSValue {
@inlinable public var jsValue: JSValue { .object(self) }
}
extension JSObject: ConstructibleFromJSValue {
@inlinable public static func construct(from value: JSValue) -> JSObject? { value.object }
}
extension JSObject {
@inlinable public var properties: [String: JSValue] { self.object }
@inlinable public func push(_ value: JSValue) {
self.buffer.append(value)
}
}
extension JSObject {
@inlinable public subscript(index: Int) -> JSValue {
get {
self.buffer.indices.contains(index) ? self.buffer[index] : .undefined
}
set(value) {
if self.buffer.endIndex < index {
let count: Int = index.distance(to: self.buffer.endIndex)
self.buffer.append(contentsOf: repeatElement(.undefined, count: count))
self.buffer.append(value)
} else if
self.buffer.endIndex == index {
self.buffer.append(value)
} else {
self.buffer[index] = value
}
}
}
@inlinable public subscript(key: JSString) -> JSValue {
get {
if self.isArray, key.string == "length" {
return .number(Double.init(self.buffer.count))
} else {
return self.object[key.string] ?? .undefined
}
}
set(value) {
if self.isArray, key.string == "length" {
guard
let length: Int = .construct(from: value) else {
fatalError("Invalid array length")
}
if length < self.buffer.count {
self.buffer.removeLast(self.buffer.count - length)
} else if length > self.buffer.count {
self.buffer.reserveCapacity(length)
self.buffer.append(
contentsOf: repeatElement(.undefined, count: length - self.buffer.count)
)
}
} else {
self.object[key.string] = value
}
}
}
}