172 lines
5.5 KiB
Swift
172 lines
5.5 KiB
Swift
/**
|
|
* 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)")
|
|
}
|
|
}
|