Moved BindingGenerator from gen html project. It makes more sense here.
This commit is contained in:
31
Sources/BindingGenerator/Internal/AppError.swift
Normal file
31
Sources/BindingGenerator/Internal/AppError.swift
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// AppError.swift
|
||||
// HRW
|
||||
//
|
||||
// Created by Isaac Paul on 10/13/24.
|
||||
// Non-commercial license, see LICENSE.MD in the project root for details
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
final class AppError: LocalizedError, Sendable {
|
||||
|
||||
let message: String
|
||||
|
||||
init(_ message: String) {
|
||||
self.message = message
|
||||
}
|
||||
init(_ message: String, _ error:Error) {
|
||||
self.message = "\(message) : \(error.localizedDescription)"
|
||||
}
|
||||
|
||||
static func failure<T>(_ message: String) -> Result<T, AppError> {
|
||||
return .failure(AppError(message))
|
||||
}
|
||||
|
||||
var errorDescription: String? {
|
||||
get {
|
||||
return message
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Plot
|
||||
* Copyright (c) John Sundell 2021
|
||||
* MIT license, see LICENSE file for details
|
||||
*/
|
||||
/*
|
||||
internal final class ElementRenderingBuffer {
|
||||
var containsChildElements = false
|
||||
|
||||
private let element: AnyElement
|
||||
private let indentation: Indentation?
|
||||
private var body = ""
|
||||
private var attributes = [AnyAttribute]()
|
||||
private var attributeIndexes = [String : Int]()
|
||||
|
||||
init(element: AnyElement, indentation: Indentation?) {
|
||||
self.element = element
|
||||
self.indentation = indentation
|
||||
}
|
||||
|
||||
func add(_ attribute: AnyAttribute) {
|
||||
if let existingIndex = attributeIndexes[attribute.name] {
|
||||
if attribute.replaceExisting {
|
||||
attributes[existingIndex].value = attribute.value
|
||||
} else if let newValue = attribute.nonEmptyValue {
|
||||
if let existingValue = attributes[existingIndex].nonEmptyValue {
|
||||
attributes[existingIndex].value = existingValue + " " + newValue
|
||||
} else {
|
||||
attributes[existingIndex].value = newValue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
attributeIndexes[attribute.name] = attributes.count
|
||||
attributes.append(attribute)
|
||||
}
|
||||
}
|
||||
|
||||
func add(_ text: String, isPlainText: Bool) {
|
||||
if !isPlainText, indentation != nil {
|
||||
body.append("\n")
|
||||
}
|
||||
|
||||
body.append(text)
|
||||
}
|
||||
|
||||
func flush() -> String {
|
||||
guard !element.name.isEmpty else { return body }
|
||||
|
||||
let whitespace = indentation?.string ?? ""
|
||||
let padding = element.paddingCharacter.map(String.init) ?? ""
|
||||
var openingTag = "\(whitespace)<\(padding)\(element.name)"
|
||||
|
||||
for attribute in attributes {
|
||||
let string = attribute.render()
|
||||
|
||||
if !string.isEmpty {
|
||||
openingTag.append(" " + string)
|
||||
}
|
||||
}
|
||||
|
||||
let openingTagSuffix = padding + ">"
|
||||
|
||||
switch element.closingMode {
|
||||
case .standard,
|
||||
.selfClosing where containsChildElements:
|
||||
var string = openingTag + openingTagSuffix + body
|
||||
|
||||
if indentation != nil && containsChildElements {
|
||||
string.append("\n\(whitespace)")
|
||||
}
|
||||
|
||||
return string + "</\(element.name)>"
|
||||
case .neverClosed:
|
||||
return openingTag + openingTagSuffix + body
|
||||
case .selfClosing:
|
||||
return openingTag + "/" + openingTagSuffix
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
45
Sources/BindingGenerator/Internal/GlobalAttributeKey.swift
Normal file
45
Sources/BindingGenerator/Internal/GlobalAttributeKey.swift
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// GlobalAttributeKey.swift
|
||||
// HRW
|
||||
//
|
||||
// Created by Isaac Paul on 10/15/24.
|
||||
// Non-commercial license, see LICENSE.MD in the project root for details
|
||||
//
|
||||
|
||||
public protocol IGlobalAttributeContainer {
|
||||
var globalAttributes:Dictionary<GlobalAttributeKey, String> { get set }
|
||||
var dataAttributes:Dictionary<String, String> { get set }
|
||||
}
|
||||
|
||||
public enum GlobalAttributeKey: String {
|
||||
case class_ = "class"
|
||||
case id
|
||||
case slot
|
||||
|
||||
case accesskey
|
||||
case autocapitalize
|
||||
case autocorrect
|
||||
case autofocus
|
||||
case contenteditable
|
||||
case dir
|
||||
case draggable
|
||||
case enterkeyhint
|
||||
case hidden
|
||||
case inert
|
||||
case inputmode
|
||||
case is_ = "is"
|
||||
case itemid
|
||||
case itemprop
|
||||
case itemref
|
||||
case itemscope
|
||||
case itemtype
|
||||
case lang
|
||||
case nonce
|
||||
case popover
|
||||
case spellcheck
|
||||
case style
|
||||
case tabindex
|
||||
case title
|
||||
case translate
|
||||
case writingsuggestions
|
||||
}
|
||||
124
Sources/BindingGenerator/Internal/GlobalEnums.swift
Normal file
124
Sources/BindingGenerator/Internal/GlobalEnums.swift
Normal file
@@ -0,0 +1,124 @@
|
||||
//
|
||||
// GlobalEnums.swift
|
||||
// HRW
|
||||
//
|
||||
// Created by Isaac Paul on 10/19/24.
|
||||
// Non-commercial license, see LICENSE.MD in the project root for details
|
||||
//
|
||||
import Foundation
|
||||
|
||||
//Some bike shedding to avoid allocations
|
||||
struct ComponentsIterator: IteratorProtocol {
|
||||
let string: String
|
||||
let splitCharacter: Character
|
||||
var currentIndex: String.Index
|
||||
|
||||
init(_ string: String, separatedBy: Character) {
|
||||
self.string = string
|
||||
self.splitCharacter = separatedBy
|
||||
self.currentIndex = string.startIndex
|
||||
}
|
||||
|
||||
mutating func next() -> Substring? {
|
||||
// Skip any leading split characters
|
||||
while currentIndex < string.endIndex && string[currentIndex] == splitCharacter {
|
||||
currentIndex = string.index(after: currentIndex)
|
||||
}
|
||||
|
||||
// Check if we've reached the end of the string
|
||||
if currentIndex >= string.endIndex {
|
||||
return nil
|
||||
}
|
||||
|
||||
let start = currentIndex
|
||||
while currentIndex < string.endIndex && string[currentIndex] != splitCharacter {
|
||||
currentIndex = string.index(after: currentIndex)
|
||||
}
|
||||
|
||||
return string[start..<currentIndex]
|
||||
}
|
||||
}
|
||||
|
||||
extension IteratorProtocol {
|
||||
mutating func map<R>(_ block: (_ input:Element) throws -> R) rethrows -> [R] {
|
||||
var result:[R] = []
|
||||
while let item = self.next() {
|
||||
result.append(try block(item))
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func componentsIterator(separatedBy: Character) -> ComponentsIterator {
|
||||
return ComponentsIterator(self, separatedBy: separatedBy)
|
||||
}
|
||||
|
||||
func componentsIterator(separatedBy: String) -> ComponentsIterator {
|
||||
return ComponentsIterator(self, separatedBy: separatedBy.first!)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum ShadowRootMode : Substring {
|
||||
case open = "open" ///The template element represents an open declarative shadow root.
|
||||
case close = "close" ///The template element represents a closed declarative shadow root.
|
||||
|
||||
public init?(rawValue: String) {
|
||||
self.init(rawValue: rawValue.asSubstring())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum Blocking : Substring {
|
||||
case render = "render"
|
||||
|
||||
static func parseList(_ value:String?) throws -> [Blocking] {
|
||||
guard let value = value else { return [] }
|
||||
var iterator = value.componentsIterator(separatedBy: " ")
|
||||
let result = try iterator.map { input in
|
||||
return try expect(Blocking(rawValue: input), "unexpected value for blocking: \(input)")
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
public enum FetchPriority : Substring {
|
||||
case high = "high"
|
||||
case low = "low"
|
||||
case auto = "auto"
|
||||
|
||||
public init?(rawValue: String) {
|
||||
self.init(rawValue: rawValue.asSubstring())
|
||||
}
|
||||
|
||||
public init(expect: String) throws {
|
||||
guard let _ = FetchPriority(rawValue: expect) else {
|
||||
throw AppError("Unexpected value for Fetch Priority: \(expect)")
|
||||
}
|
||||
self.init(rawValue: expect.asSubstring())!
|
||||
}
|
||||
}
|
||||
|
||||
public enum Preload : Substring {
|
||||
case none = "none"
|
||||
case auto = "auto"
|
||||
case metadata = "metadata"
|
||||
|
||||
public init?(rawValue: String) {
|
||||
self.init(rawValue: rawValue.asSubstring())
|
||||
}
|
||||
|
||||
public init(expect: String) throws {
|
||||
let strToUse:String
|
||||
if (expect.count == 0) {
|
||||
strToUse = "auto"
|
||||
} else {
|
||||
strToUse = expect
|
||||
}
|
||||
guard let _ = Preload(rawValue: strToUse) else {
|
||||
throw AppError("Unexpected value for Preload \(strToUse)")
|
||||
}
|
||||
self.init(rawValue: strToUse.asSubstring())!
|
||||
}
|
||||
}
|
||||
92
Sources/BindingGenerator/Internal/GlobalEventKey.swift
Normal file
92
Sources/BindingGenerator/Internal/GlobalEventKey.swift
Normal file
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// GlobalEventKey.swift
|
||||
// HRW
|
||||
//
|
||||
// Created by Isaac Paul on 10/22/24.
|
||||
// Non-commercial license, see LICENSE.MD in the project root for details
|
||||
//
|
||||
|
||||
public protocol IGlobalEventContainer {
|
||||
var globalEvents:Dictionary<GlobalEventKey, String> { get set }
|
||||
}
|
||||
|
||||
///While these attributes apply to all elements, they are not useful on all elements. For example, only media elements will ever receive a volumechange event fired by the user agent.
|
||||
public enum GlobalEventKey: Substring {
|
||||
case onauxclick
|
||||
case onbeforeinput
|
||||
case onbeforematch
|
||||
case onbeforetoggle
|
||||
/// when specified on body elements it exposes event handlers of the Window object with the same names.
|
||||
case onblur
|
||||
case oncancel
|
||||
case oncanplay
|
||||
case oncanplaythrough
|
||||
case onchange
|
||||
case onclick
|
||||
case onclose
|
||||
case oncontextlost
|
||||
case oncontextmenu
|
||||
case oncontextrestored
|
||||
case oncopy
|
||||
case oncuechange
|
||||
case oncut
|
||||
case ondblclick
|
||||
case ondrag
|
||||
case ondragend
|
||||
case ondragenter
|
||||
case ondragleave
|
||||
case ondragover
|
||||
case ondragstart
|
||||
case ondrop
|
||||
case ondurationchange
|
||||
case onemptied
|
||||
case onended
|
||||
/// when specified on body elements it exposes event handlers of the Window object with the same names.
|
||||
case onerror
|
||||
/// when specified on body elements it exposes event handlers of the Window object with the same names.
|
||||
case onfocus
|
||||
case onformdata
|
||||
case oninput
|
||||
case oninvalid
|
||||
case onkeydown
|
||||
case onkeypress
|
||||
case onkeyup
|
||||
/// when specified on body elements it exposes event handlers of the Window object with the same names.
|
||||
case onload
|
||||
case onloadeddata
|
||||
case onloadedmetadata
|
||||
case onloadstart
|
||||
case onmousedown
|
||||
case onmouseenter
|
||||
case onmouseleave
|
||||
case onmousemove
|
||||
case onmouseout
|
||||
case onmouseover
|
||||
case onmouseup
|
||||
case onpaste
|
||||
case onpause
|
||||
case onplay
|
||||
case onplaying
|
||||
case onprogress
|
||||
case onratechange
|
||||
case onreset
|
||||
/// when specified on body elements it exposes event handlers of the Window object with the same names.
|
||||
case onresize
|
||||
/// when specified on body elements it exposes event handlers of the Window object with the same names.
|
||||
case onscroll
|
||||
/// when specified on body elements it exposes event handlers of the Window object with the same names.
|
||||
case onscrollend
|
||||
case onsecuritypolicyviolation
|
||||
case onseeked
|
||||
case onseeking
|
||||
case onselect
|
||||
case onslotchange
|
||||
case onstalled
|
||||
case onsubmit
|
||||
case onsuspend
|
||||
case ontimeupdate
|
||||
case ontoggle
|
||||
case onvolumechange
|
||||
case onwaiting
|
||||
case onwheel
|
||||
}
|
||||
171
Sources/BindingGenerator/Internal/String+Escaping.swift
Normal file
171
Sources/BindingGenerator/Internal/String+Escaping.swift
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Plot
|
||||
* Copyright (c) John Sundell 2019
|
||||
* MIT license, see LICENSE file for details
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// let someUrl:URL = try URL("hello") ?! AppError("What")
|
||||
infix operator ?!: NilCoalescingPrecedence
|
||||
|
||||
public func ?!<T>(value: T?, error: @autoclosure () -> Error) throws -> T {
|
||||
if let value = value { return value }
|
||||
throw error()
|
||||
}
|
||||
|
||||
|
||||
internal extension String {
|
||||
func asSubstring() -> Substring {
|
||||
return self[self.startIndex..<self.endIndex]
|
||||
}
|
||||
func escaped() -> String {
|
||||
var pendingAmpersandString: String?
|
||||
|
||||
func flushPendingAmpersandString(
|
||||
withSuffix suffix: String? = nil,
|
||||
resettingTo newValue: String? = nil
|
||||
) -> String {
|
||||
let pending = pendingAmpersandString
|
||||
pendingAmpersandString = newValue
|
||||
return pending.map { "&\($0)\(suffix ?? "")" } ?? suffix ?? ""
|
||||
}
|
||||
|
||||
return String(flatMap { character -> String in
|
||||
switch character {
|
||||
case "<":
|
||||
return flushPendingAmpersandString(withSuffix: "<")
|
||||
case ">":
|
||||
return flushPendingAmpersandString(withSuffix: ">")
|
||||
case "&":
|
||||
return flushPendingAmpersandString(resettingTo: "")
|
||||
case ";":
|
||||
let pending = pendingAmpersandString.map { "&\($0);" }
|
||||
pendingAmpersandString = nil
|
||||
return pending ?? ";"
|
||||
case "#" where pendingAmpersandString?.isEmpty == true:
|
||||
pendingAmpersandString = "#"
|
||||
return ""
|
||||
default:
|
||||
if let pending = pendingAmpersandString {
|
||||
guard character.isLetter || character.isNumber else {
|
||||
return flushPendingAmpersandString(withSuffix: String(character))
|
||||
}
|
||||
|
||||
pendingAmpersandString = "\(pending)\(character)"
|
||||
return ""
|
||||
}
|
||||
|
||||
return "\(character)"
|
||||
}
|
||||
}) + flushPendingAmpersandString()
|
||||
}
|
||||
|
||||
static func parseList(_ value:String?, _ separator:String = " ") throws -> [String] {
|
||||
guard let value = value else { return [] }
|
||||
var iterator = value.componentsIterator(separatedBy: separator)
|
||||
return iterator.map({String($0)})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Array where Element: RawRepresentable, Element.RawValue == String {
|
||||
/// Converts an array of String-backed enums to an array of their raw string values.
|
||||
func toStringList(_ seperator:String) -> String {
|
||||
let strList = self.map { $0.rawValue }
|
||||
return strList.joined(separator: seperator)
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element == String {
|
||||
/// Converts an array of String-backed enums to an array of their raw string values.
|
||||
func toStringList(_ seperator:String) -> String {
|
||||
let strList = self.map { $0 }
|
||||
return strList.joined(separator: seperator)
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element == URL {
|
||||
/// Converts an array of String-backed enums to an array of their raw string values.
|
||||
func toStringList(_ seperator:String) -> String {
|
||||
let strList = self.map { $0.absoluteString }
|
||||
return strList.joined(separator: seperator)
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element == Float {
|
||||
/// Converts an array of String-backed enums to an array of their raw string values.
|
||||
func toStringList(_ seperator:String) -> String {
|
||||
let strList = self.map { String($0) }
|
||||
return strList.joined(separator: seperator)
|
||||
}
|
||||
}
|
||||
|
||||
extension Set where Element: RawRepresentable, Element.RawValue == String {
|
||||
/// Converts an array of String-backed enums to an array of their raw string values.
|
||||
func toStringList(_ seperator:String) -> String {
|
||||
let strList = self.map { $0.rawValue }
|
||||
return strList.joined(separator: seperator)
|
||||
}
|
||||
}
|
||||
|
||||
extension Float {
|
||||
static func parseList(_ value:String?, _ separator:String = " ") throws -> [Float] {
|
||||
guard let value = value else { return [] }
|
||||
var iterator = value.componentsIterator(separatedBy: separator)
|
||||
return try iterator.map({ try Float.expect($0) })
|
||||
}
|
||||
|
||||
static func expect(_ value:String) throws -> Float {
|
||||
if let result = Float(value) {
|
||||
return result
|
||||
}
|
||||
throw AppError("Unable to map value to float")
|
||||
}
|
||||
|
||||
static func expect(_ value:Substring) throws -> Float {
|
||||
if let result = Float(value) {
|
||||
return result
|
||||
}
|
||||
throw AppError("Unable to map value to float")
|
||||
}
|
||||
}
|
||||
|
||||
extension URL {
|
||||
static func parseList(_ value:String?, _ separator:String = " ") throws -> [URL] {
|
||||
guard let value = value else { return [] }
|
||||
var iterator = value.componentsIterator(separatedBy: separator)
|
||||
return try iterator.map({
|
||||
guard let result = URL(string:String($0)) else {
|
||||
throw AppError("Invalid URL: \(String($0))")
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
extension Bool {
|
||||
public init(expect: String) throws {
|
||||
if (expect == "true") {
|
||||
self = true
|
||||
return
|
||||
}
|
||||
if (expect == "false") {
|
||||
self = false
|
||||
return
|
||||
}
|
||||
throw AppError("Unexpected value for bool: \(expect)")
|
||||
}
|
||||
|
||||
public init(expectYesOrNo: String) throws {
|
||||
if (expectYesOrNo == "yes") {
|
||||
self = true
|
||||
return
|
||||
}
|
||||
if (expectYesOrNo == "no") {
|
||||
self = false
|
||||
return
|
||||
}
|
||||
throw AppError("Unexpected value for bool: \(expectYesOrNo)")
|
||||
}
|
||||
}
|
||||
419
Sources/BindingGenerator/Internal/TinyArray.swift
Normal file
419
Sources/BindingGenerator/Internal/TinyArray.swift
Normal file
@@ -0,0 +1,419 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the SwiftCertificates open source project
|
||||
//
|
||||
// Copyright (c) 2023 Apple Inc. and the SwiftCertificates project authors
|
||||
// Licensed under Apache License v2.0
|
||||
//
|
||||
// See LICENSE.txt for license information
|
||||
// See CONTRIBUTORS.txt for the list of SwiftCertificates project authors
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//Taken from: https://github.com/apple/swift-certificates
|
||||
|
||||
/// ``_TinyArray`` is a ``RandomAccessCollection`` optimised to store zero or one ``Element``.
|
||||
/// It supports arbitrary many elements but if only up to one ``Element`` is stored it does **not** allocate separate storage on the heap
|
||||
/// and instead stores the ``Element`` inline.
|
||||
public struct _TinyArray<Element> {
|
||||
@usableFromInline
|
||||
enum Storage {
|
||||
case one(Element)
|
||||
case arbitrary([Element])
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
var storage: Storage
|
||||
}
|
||||
|
||||
// MARK: - TinyArray "public" interface
|
||||
|
||||
extension _TinyArray: Equatable where Element: Equatable {}
|
||||
extension _TinyArray: Hashable where Element: Hashable {}
|
||||
extension _TinyArray: Sendable where Element: Sendable {}
|
||||
|
||||
extension _TinyArray: ExpressibleByArrayLiteral {
|
||||
@inlinable
|
||||
public init(arrayLiteral elements: Element...) {
|
||||
switch elements.count {
|
||||
case 0:
|
||||
self = .init()
|
||||
case 1:
|
||||
self = .init(CollectionOfOne(elements[0]))
|
||||
default:
|
||||
self = .init(elements)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _TinyArray: RandomAccessCollection {
|
||||
public typealias Element = Element
|
||||
|
||||
public typealias Index = Int
|
||||
|
||||
@inlinable
|
||||
public subscript(position: Int) -> Element {
|
||||
get {
|
||||
self.storage[position]
|
||||
}
|
||||
set {
|
||||
self.storage[position] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public var startIndex: Int {
|
||||
self.storage.startIndex
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public var endIndex: Int {
|
||||
self.storage.endIndex
|
||||
}
|
||||
}
|
||||
|
||||
extension _TinyArray {
|
||||
@inlinable
|
||||
public init(_ elements: some Sequence<Element>) {
|
||||
self.storage = .init(elements)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public init(_ elements: some Sequence<Result<Element, some Error>>) throws {
|
||||
self.storage = try .init(elements)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public init() {
|
||||
self.storage = .init()
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public mutating func append(_ newElement: Element) {
|
||||
self.storage.append(newElement)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public mutating func append(contentsOf newElements: some Sequence<Element>) {
|
||||
self.storage.append(contentsOf: newElements)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public mutating func remove(at index: Int) -> Element {
|
||||
self.storage.remove(at: index)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public mutating func removeAll(where shouldBeRemoved: (Element) throws -> Bool) rethrows {
|
||||
try self.storage.removeAll(where: shouldBeRemoved)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public mutating func sort(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows {
|
||||
try self.storage.sort(by: areInIncreasingOrder)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func flatMap<SegmentOfResult>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> _TinyArray<SegmentOfResult.Element> where SegmentOfResult : Sequence {
|
||||
try self.storage.flatMap(transform)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func flatMap<NEWTYPE>(_ transform: (Element) throws -> _TinyArray<NEWTYPE>) rethrows -> _TinyArray<NEWTYPE> where Element == _TinyArray<NEWTYPE> {
|
||||
try self.storage.flatMap(transform)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func flatMap<NEWTYPE>() -> _TinyArray<NEWTYPE> where Element == _TinyArray<NEWTYPE> {
|
||||
self.storage.flatMap()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TinyArray.Storage "private" implementation
|
||||
|
||||
extension _TinyArray.Storage: Equatable where Element: Equatable {
|
||||
@inlinable
|
||||
static func == (lhs: Self, rhs: Self) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.one(let lhs), .one(let rhs)):
|
||||
return lhs == rhs
|
||||
case (.arbitrary(let lhs), .arbitrary(let rhs)):
|
||||
// we don't use lhs.elementsEqual(rhs) so we can hit the fast path from Array
|
||||
// if both arrays share the same underlying storage: https://github.com/apple/swift/blob/b42019005988b2d13398025883e285a81d323efa/stdlib/public/core/Array.swift#L1775
|
||||
return lhs == rhs
|
||||
|
||||
case (.one(let element), .arbitrary(let array)),
|
||||
(.arbitrary(let array), .one(let element)):
|
||||
guard array.count == 1 else {
|
||||
return false
|
||||
}
|
||||
return element == array[0]
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
extension _TinyArray.Storage: Hashable where Element: Hashable {
|
||||
@inlinable
|
||||
func hash(into hasher: inout Hasher) {
|
||||
// same strategy as Array: https://github.com/apple/swift/blob/b42019005988b2d13398025883e285a81d323efa/stdlib/public/core/Array.swift#L1801
|
||||
hasher.combine(count)
|
||||
for element in self {
|
||||
hasher.combine(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
extension _TinyArray.Storage: Sendable where Element: Sendable {}
|
||||
|
||||
extension _TinyArray.Storage: RandomAccessCollection {
|
||||
@inlinable
|
||||
subscript(position: Int) -> Element {
|
||||
get {
|
||||
switch self {
|
||||
case .one(let element):
|
||||
guard position == 0 else {
|
||||
fatalError("index \(position) out of bounds")
|
||||
}
|
||||
return element
|
||||
case .arbitrary(let elements):
|
||||
return elements[position]
|
||||
}
|
||||
}
|
||||
set {
|
||||
switch self {
|
||||
case .one:
|
||||
guard position == 0 else {
|
||||
fatalError("index \(position) out of bounds")
|
||||
}
|
||||
self = .one(newValue)
|
||||
case .arbitrary(var elements):
|
||||
elements[position] = newValue
|
||||
self = .arbitrary(elements)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
var startIndex: Int {
|
||||
0
|
||||
}
|
||||
|
||||
@inlinable
|
||||
var endIndex: Int {
|
||||
switch self {
|
||||
case .one: return 1
|
||||
case .arbitrary(let elements): return elements.endIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _TinyArray.Storage {
|
||||
@inlinable
|
||||
init(_ elements: some Sequence<Element>) {
|
||||
self = .arbitrary([])
|
||||
self.append(contentsOf: elements)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
init(_ newElements: some Sequence<Result<Element, some Error>>) throws {
|
||||
var iterator = newElements.makeIterator()
|
||||
guard let firstElement = try iterator.next()?.get() else {
|
||||
self = .arbitrary([])
|
||||
return
|
||||
}
|
||||
guard let secondElement = try iterator.next()?.get() else {
|
||||
// newElements just contains a single element
|
||||
// and we hit the fast path
|
||||
self = .one(firstElement)
|
||||
return
|
||||
}
|
||||
|
||||
var elements: [Element] = []
|
||||
elements.reserveCapacity(newElements.underestimatedCount)
|
||||
elements.append(firstElement)
|
||||
elements.append(secondElement)
|
||||
while let nextElement = try iterator.next()?.get() {
|
||||
elements.append(nextElement)
|
||||
}
|
||||
self = .arbitrary(elements)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
init() {
|
||||
self = .arbitrary([])
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func append(_ newElement: Element) {
|
||||
self.append(contentsOf: CollectionOfOne(newElement))
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func append(contentsOf newElements: some Sequence<Element>) {
|
||||
switch self {
|
||||
case .one(let firstElement):
|
||||
var iterator = newElements.makeIterator()
|
||||
guard let secondElement = iterator.next() else {
|
||||
// newElements is empty, nothing to do
|
||||
return
|
||||
}
|
||||
var elements: [Element] = []
|
||||
elements.reserveCapacity(1 + newElements.underestimatedCount)
|
||||
elements.append(firstElement)
|
||||
elements.append(secondElement)
|
||||
elements.appendRemainingElements(from: &iterator)
|
||||
self = .arbitrary(elements)
|
||||
|
||||
case .arbitrary(var elements):
|
||||
if elements.isEmpty {
|
||||
// if `self` is currently empty and `newElements` just contains a single
|
||||
// element, we skip allocating an array and set `self` to `.one(firstElement)`
|
||||
var iterator = newElements.makeIterator()
|
||||
guard let firstElement = iterator.next() else {
|
||||
// newElements is empty, nothing to do
|
||||
return
|
||||
}
|
||||
guard let secondElement = iterator.next() else {
|
||||
// newElements just contains a single element
|
||||
// and we hit the fast path
|
||||
self = .one(firstElement)
|
||||
return
|
||||
}
|
||||
elements.reserveCapacity(elements.count + newElements.underestimatedCount)
|
||||
elements.append(firstElement)
|
||||
elements.append(secondElement)
|
||||
elements.appendRemainingElements(from: &iterator)
|
||||
self = .arbitrary(elements)
|
||||
|
||||
} else {
|
||||
elements.append(contentsOf: newElements)
|
||||
self = .arbitrary(elements)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inlinable
|
||||
mutating func remove(at index: Int) -> Element {
|
||||
switch self {
|
||||
case .one(let oldElement):
|
||||
guard index == 0 else {
|
||||
fatalError("index \(index) out of bounds")
|
||||
}
|
||||
self = .arbitrary([])
|
||||
return oldElement
|
||||
|
||||
case .arbitrary(var elements):
|
||||
defer {
|
||||
self = .arbitrary(elements)
|
||||
}
|
||||
return elements.remove(at: index)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func removeAll(where shouldBeRemoved: (Element) throws -> Bool) rethrows {
|
||||
switch self {
|
||||
case .one(let oldElement):
|
||||
if try shouldBeRemoved(oldElement) {
|
||||
self = .arbitrary([])
|
||||
}
|
||||
|
||||
case .arbitrary(var elements):
|
||||
defer {
|
||||
self = .arbitrary(elements)
|
||||
}
|
||||
return try elements.removeAll(where: shouldBeRemoved)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func sort(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows {
|
||||
switch self {
|
||||
case .one:
|
||||
// a collection of just one element is always sorted, nothing to do
|
||||
break
|
||||
case .arbitrary(var elements):
|
||||
defer {
|
||||
self = .arbitrary(elements)
|
||||
}
|
||||
|
||||
try elements.sort(by: areInIncreasingOrder)
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func flatMap<SegmentOfResult>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> _TinyArray<SegmentOfResult.Element> where SegmentOfResult : Sequence {
|
||||
switch self {
|
||||
case .one(let element):
|
||||
let sequence:SegmentOfResult = try transform(element)
|
||||
let result:_TinyArray<SegmentOfResult.Element> = _TinyArray<SegmentOfResult.Element>(sequence)
|
||||
return result
|
||||
case .arbitrary(let elements):
|
||||
let seq:Array<SegmentOfResult.Element> = try elements.flatMap({ try transform($0) })
|
||||
let result:_TinyArray<SegmentOfResult.Element> = _TinyArray<SegmentOfResult.Element>(seq)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func flatMap<NEWTYPE>(_ transform: (Element) throws -> _TinyArray<NEWTYPE>) rethrows -> _TinyArray<NEWTYPE> where Element == _TinyArray<NEWTYPE> {
|
||||
switch self {
|
||||
case .one(let element):
|
||||
let test:_TinyArray<NEWTYPE> = element
|
||||
let sequence:_TinyArray<NEWTYPE> = try transform(element)
|
||||
return sequence
|
||||
case .arbitrary(let elements):
|
||||
let seq:Array<NEWTYPE> = try elements.flatMap({ try transform($0) })
|
||||
let result:_TinyArray<NEWTYPE> = _TinyArray<NEWTYPE>(seq)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func flatMap<NEWTYPE>() -> _TinyArray<NEWTYPE> where Element == _TinyArray<NEWTYPE> {
|
||||
switch self {
|
||||
case .one(let element):
|
||||
return element
|
||||
case .arbitrary(let elements):
|
||||
let seq:Array<NEWTYPE> = elements.flatMap({ $0 })
|
||||
let result:_TinyArray<NEWTYPE> = _TinyArray<NEWTYPE>(seq)
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Array {
|
||||
|
||||
@inlinable
|
||||
func flatMap<NEWTYPE>() -> Array<NEWTYPE> where Element == Array<NEWTYPE> {
|
||||
if (self.count == 1) {
|
||||
return self.first!
|
||||
} else {
|
||||
let arr = self.flatMap { $0 }
|
||||
return arr
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func appendRemainingElements(from iterator: inout some IteratorProtocol<Element>) {
|
||||
while let nextElement = iterator.next() {
|
||||
append(nextElement)
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func flatMapToTiny<NEWTYPE>() -> _TinyArray<NEWTYPE> where Element == _TinyArray<NEWTYPE> {
|
||||
if (self.count == 1) {
|
||||
return self.first!
|
||||
} else {
|
||||
let arr = self.flatMap({ $0 })
|
||||
return _TinyArray(arr)
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Sources/BindingGenerator/Internal/Url+Expect.swift
Normal file
19
Sources/BindingGenerator/Internal/Url+Expect.swift
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Url+Expect.swift
|
||||
// HRW
|
||||
//
|
||||
// Created by Isaac Paul on 7/5/25.
|
||||
// Non-commercial license, see LICENSE.MD in the project root for details
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension URL {
|
||||
init(expect: String) throws {
|
||||
if let result2 = URL(string: expect) {
|
||||
self = result2
|
||||
return
|
||||
}
|
||||
throw AppError("Could not convert to url: \(expect)")
|
||||
}
|
||||
}
|
||||
22
Sources/BindingGenerator/Internal/Util.swift
Normal file
22
Sources/BindingGenerator/Internal/Util.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Util.swift
|
||||
// HRW
|
||||
//
|
||||
// Created by Isaac Paul on 10/15/24.
|
||||
// Non-commercial license, see LICENSE.MD in the project root for details
|
||||
//
|
||||
|
||||
@inline(__always)
|
||||
internal func expect<T>(_ item:T?, _ error:String) throws -> T {
|
||||
if let item = item {
|
||||
return item
|
||||
}
|
||||
throw AppError(error)
|
||||
}
|
||||
/*
|
||||
internal func parseBool(_ item:String?) throws -> Bool? {
|
||||
if let item = item {
|
||||
return item
|
||||
}
|
||||
return nil
|
||||
}*/
|
||||
Reference in New Issue
Block a user