|
5 | 5 | // Created by Evan Anderson on 11/19/24. |
6 | 6 | // |
7 | 7 |
|
| 8 | +import SwiftSyntax |
| 9 | + |
8 | 10 | public enum HTMLElementAttribute : Hashable { |
9 | 11 | case accesskey(String? = nil) |
10 | 12 |
|
@@ -55,15 +57,18 @@ public enum HTMLElementAttribute : Hashable { |
55 | 57 | @available(*, deprecated, message: "General consensus considers this \"bad practice\" and you shouldn't mix your HTML and JavaScript. This will never be removed and remains deprecated to encourage use of other techniques. Learn more at https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#inline_event_handlers_—_dont_use_these.") |
56 | 58 | case event(Extra.event, _ value: String? = nil) |
57 | 59 |
|
58 | | - public init?(rawValue: String) { |
59 | | - guard rawValue.last == ")" else { return nil } |
60 | | - let key:Substring = rawValue.split(separator: "(")[0] |
61 | | - func string() -> String { HTMLElementAttribute.string(key: key, rawValue: rawValue) } |
62 | | - func boolean() -> Bool { HTMLElementAttribute.boolean(key: key, rawValue: rawValue) } |
63 | | - func enumeration<T : HTMLInitializable>() -> T { HTMLElementAttribute.enumeration(key: key, rawValue: rawValue) } |
64 | | - func int() -> Int { HTMLElementAttribute.int(key: key, rawValue: rawValue) } |
65 | | - func array_string() -> [String] { HTMLElementAttribute.array_string(key: key, rawValue: rawValue) } |
66 | | - func float() -> Float { HTMLElementAttribute.float(key: key, rawValue: rawValue) } |
| 60 | + // MARK: init rawValue |
| 61 | + public init?(key: String, _ function: FunctionCallExprSyntax) { |
| 62 | + let expression:ExprSyntax = function.arguments.first!.expression |
| 63 | + func string() -> String { expression.stringLiteral!.string } |
| 64 | + func boolean() -> Bool { expression.booleanLiteral!.literal.text == "true" } |
| 65 | + func enumeration<T : HTMLInitializable>() -> T { |
| 66 | + let function:FunctionCallExprSyntax = expression.functionCall! |
| 67 | + return T(key: function.calledExpression.memberAccess!.declName.baseName.text, arguments: function.arguments)! |
| 68 | + } |
| 69 | + func int() -> Int { Int(expression.integerLiteral!.literal.text) ?? -1 } |
| 70 | + func array_string() -> [String] { expression.array!.elements.map({ $0.expression.stringLiteral!.string }) } |
| 71 | + func float() -> Float { Float(expression.floatLiteral!.literal.text) ?? -1 } |
67 | 72 | switch key { |
68 | 73 | case "accesskey": self = .accesskey(string()) |
69 | 74 | case "ariaattribute": self = .ariaattribute(enumeration()) |
@@ -107,6 +112,7 @@ public enum HTMLElementAttribute : Hashable { |
107 | 112 | } |
108 | 113 | } |
109 | 114 |
|
| 115 | + // MARK: key |
110 | 116 | public var key : String { |
111 | 117 | switch self { |
112 | 118 | case .accesskey(_): return "accesskey" |
@@ -162,6 +168,7 @@ public enum HTMLElementAttribute : Hashable { |
162 | 168 | } |
163 | 169 | } |
164 | 170 |
|
| 171 | + // MARK: htmlValue |
165 | 172 | public var htmlValue : String? { |
166 | 173 | switch self { |
167 | 174 | case .accesskey(let value): return value |
@@ -211,55 +218,27 @@ public enum HTMLElementAttribute : Hashable { |
211 | 218 | extension HTMLElementAttribute { |
212 | 219 | public enum Extra { |
213 | 220 | } |
214 | | - |
215 | | - static func literal(key: Substring, rawValue: String) -> String { |
216 | | - let start:String.Index = rawValue.startIndex, end:String.Index = rawValue.index(before: rawValue.endIndex) |
217 | | - return String(rawValue[rawValue.index(start, offsetBy: key.count + 2)..<end]) |
218 | | - } |
219 | | - static func string(key: Substring, rawValue: String) -> String { |
220 | | - let start:String.Index = rawValue.startIndex, end:String.Index = rawValue.index(before: rawValue.endIndex), end_minus_one:String.Index = rawValue.index(before: end) |
221 | | - return String(rawValue[rawValue.index(start, offsetBy: key.count + 2)..<end_minus_one]) |
222 | | - } |
223 | | - static func boolean(key: Substring, rawValue: String) -> Bool { |
224 | | - let start:String.Index = rawValue.startIndex, end:String.Index = rawValue.index(before: rawValue.endIndex) |
225 | | - return rawValue[rawValue.index(start, offsetBy: key.count + 1)..<end] == "true" |
226 | | - } |
227 | | - static func enumeration<T : HTMLInitializable>(key: Substring, rawValue: String) -> T { |
228 | | - let start:String.Index = rawValue.startIndex, end:String.Index = rawValue.index(before: rawValue.endIndex) |
229 | | - return T(rawValue: String(rawValue[rawValue.index(start, offsetBy: key.count + 2)..<end]))! |
230 | | - } |
231 | | - static func int(key: Substring, rawValue: String) -> Int { |
232 | | - let start:String.Index = rawValue.startIndex, end:String.Index = rawValue.index(before: rawValue.endIndex) |
233 | | - return Int(rawValue[rawValue.index(start, offsetBy: key.count + 1)..<end])! |
234 | | - } |
235 | | - static func array_string(key: Substring, rawValue: String) -> [String] { |
236 | | - let string:String = string(key: key, rawValue: rawValue) |
237 | | - let ranges:[Range<String.Index>] = try! string.ranges(of: Regex("\"([^\"]+)\"")) // TODO: fix? (doesn't parse correctly if the string contains escaped quotation marks) |
238 | | - return ranges.map({ |
239 | | - let item:String = String(string[$0]) |
240 | | - return String(item[item.index(after: item.startIndex)..<item.index(before: item.endIndex)]) |
241 | | - }) |
242 | | - } |
243 | | - static func float(key: Substring, rawValue: String) -> Float { |
244 | | - let start:String.Index = rawValue.startIndex, end:String.Index = rawValue.index(before: rawValue.endIndex) |
245 | | - return Float(rawValue[rawValue.index(start, offsetBy: key.count + 1)..<end])! |
246 | | - } |
247 | 221 | } |
248 | 222 | public protocol HTMLInitializable : Hashable { |
249 | | - init?(rawValue: String) |
| 223 | + init?(key: String, arguments: LabeledExprListSyntax) |
250 | 224 |
|
251 | 225 | var key : String { get } |
252 | 226 | var htmlValue : String? { get } |
253 | 227 | } |
254 | 228 | public extension HTMLInitializable where Self: RawRepresentable, RawValue == String { |
255 | 229 | var key : String { rawValue } |
256 | 230 | var htmlValue : String? { rawValue } |
| 231 | + |
| 232 | + init?(key: String, arguments: LabeledExprListSyntax) { |
| 233 | + guard let value:Self = .init(rawValue: key) else { return nil } |
| 234 | + self = value |
| 235 | + } |
257 | 236 | } |
258 | 237 | public extension HTMLElementAttribute.Extra { |
259 | 238 | typealias height = HTMLElementAttribute.CSSUnit |
260 | 239 | typealias width = HTMLElementAttribute.CSSUnit |
261 | 240 |
|
262 | | - // MARK: aria attributes (states and properties) |
| 241 | + // MARK: aria attributes |
263 | 242 | // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes |
264 | 243 | enum ariaattribute : HTMLInitializable { |
265 | 244 | case activedescendant(String) |
@@ -333,15 +312,17 @@ public extension HTMLElementAttribute.Extra { |
333 | 312 | case valuenow(Float) |
334 | 313 | case valuetext(String) |
335 | 314 |
|
336 | | - public init?(rawValue: String) { |
337 | | - guard rawValue.last == ")" else { return nil } |
338 | | - let key:Substring = rawValue.split(separator: "(")[0] |
339 | | - func string() -> String { HTMLElementAttribute.string(key: key, rawValue: rawValue) } |
340 | | - func boolean() -> Bool { HTMLElementAttribute.boolean(key: key, rawValue: rawValue) } |
341 | | - func enumeration<T : HTMLInitializable>() -> T { HTMLElementAttribute.enumeration(key: key, rawValue: rawValue) } |
342 | | - func int() -> Int { HTMLElementAttribute.int(key: key, rawValue: rawValue) } |
343 | | - func array_string() -> [String] { HTMLElementAttribute.array_string(key: key, rawValue: rawValue) } |
344 | | - func float() -> Float { HTMLElementAttribute.float(key: key, rawValue: rawValue) } |
| 315 | + public init?(key: String, arguments: LabeledExprListSyntax) { |
| 316 | + let expression:ExprSyntax = arguments.first!.expression |
| 317 | + func string() -> String { expression.stringLiteral!.string } |
| 318 | + func boolean() -> Bool { expression.booleanLiteral!.literal.text == "true" } |
| 319 | + func enumeration<T : HTMLInitializable>() -> T { |
| 320 | + let function:FunctionCallExprSyntax = expression.functionCall! |
| 321 | + return T(key: function.calledExpression.memberAccess!.declName.baseName.text, arguments: function.arguments)! |
| 322 | + } |
| 323 | + func int() -> Int { Int(expression.integerLiteral!.literal.text) ?? -1 } |
| 324 | + func array_string() -> [String] { expression.array!.elements.map({ $0.expression.stringLiteral!.string }) } |
| 325 | + func float() -> Float { Float(expression.floatLiteral!.literal.text) ?? -1 } |
345 | 326 | switch key { |
346 | 327 | case "activedescendant": self = .activedescendant(string()) |
347 | 328 | case "atomic": self = .atomic(boolean()) |
@@ -723,20 +704,15 @@ public extension HTMLElementAttribute.Extra { |
723 | 704 | case togglePopover |
724 | 705 | case custom(String) |
725 | 706 |
|
726 | | - public init?(rawValue: String) { |
727 | | - switch rawValue { |
| 707 | + public init?(key: String, arguments: LabeledExprListSyntax) { |
| 708 | + switch key { |
728 | 709 | case "showModal": self = .showModal |
729 | 710 | case "close": self = .close |
730 | 711 | case "showPopover": self = .showPopover |
731 | 712 | case "hidePopover": self = .hidePopover |
732 | 713 | case "togglePopover": self = .togglePopover |
733 | | - default: |
734 | | - if rawValue.starts(with: "custom(\"") && rawValue.hasSuffix("\")") { |
735 | | - let value:String = String(rawValue[rawValue.index(rawValue.startIndex, offsetBy: 8)..<rawValue.index(rawValue.endIndex, offsetBy: -2)]) |
736 | | - self = .custom(value) |
737 | | - } else { |
738 | | - return nil |
739 | | - } |
| 714 | + case "custom": self = .custom(arguments.first!.expression.stringLiteral!.string) |
| 715 | + default: return nil |
740 | 716 | } |
741 | 717 | } |
742 | 718 |
|
@@ -819,16 +795,11 @@ public extension HTMLElementAttribute.Extra { |
819 | 795 | case empty |
820 | 796 | case filename(String) |
821 | 797 |
|
822 | | - public init?(rawValue: String) { |
823 | | - if rawValue == "empty" { |
824 | | - self = .empty |
825 | | - } else { |
826 | | - if rawValue.starts(with: "filename(\"") && rawValue.hasSuffix("\")") { |
827 | | - let value:String = String(rawValue[rawValue.index(rawValue.startIndex, offsetBy: 10)..<rawValue.index(rawValue.endIndex, offsetBy: -2)]) |
828 | | - self = .filename(value) |
829 | | - } else { |
830 | | - return nil |
831 | | - } |
| 798 | + public init?(key: String, arguments: LabeledExprListSyntax) { |
| 799 | + switch key { |
| 800 | + case "empty": self = .empty |
| 801 | + case "filename": self = .filename(arguments.first!.expression.stringLiteral!.string) |
| 802 | + default: return nil |
832 | 803 | } |
833 | 804 | } |
834 | 805 |
|
|
0 commit comments