Remove Foundation dependency
This commit is contained in:
parent
740d49c99b
commit
1f5cc7ac33
2
.bazelrc
2
.bazelrc
|
@ -1,3 +1,3 @@
|
||||||
build --macos_minimum_os=10.9
|
build --macos_minimum_os=13.0
|
||||||
build --repo_env=CC=clang
|
build --repo_env=CC=clang
|
||||||
test --test_output=errors
|
test --test_output=errors
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// swift-tools-version:5.4
|
// swift-tools-version:5.7
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
let dependencies: [Package.Dependency]
|
let dependencies: [Package.Dependency]
|
||||||
|
@ -10,6 +10,7 @@ dependencies = []
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "Yams",
|
name: "Yams",
|
||||||
|
platforms: [.macOS(.v13)],
|
||||||
products: [
|
products: [
|
||||||
.library(name: "Yams", targets: ["Yams"]),
|
.library(name: "Yams", targets: ["Yams"]),
|
||||||
.executable(name: "yams-cli", targets: ["yams-cli"])
|
.executable(name: "yams-cli", targets: ["yams-cli"])
|
||||||
|
@ -29,14 +30,6 @@ let package = Package(
|
||||||
name: "Yams",
|
name: "Yams",
|
||||||
dependencies: ["CYaml"],
|
dependencies: ["CYaml"],
|
||||||
exclude: ["CMakeLists.txt"]
|
exclude: ["CMakeLists.txt"]
|
||||||
),
|
|
||||||
.testTarget(
|
|
||||||
name: "YamsTests",
|
|
||||||
dependencies: ["Yams"],
|
|
||||||
exclude: ["CMakeLists.txt"],
|
|
||||||
resources: [
|
|
||||||
.copy("Fixtures/SourceKitten#289/debug.yaml"),
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
// Copyright (c) 2016 Yams. All rights reserved.
|
// Copyright (c) 2016 Yams. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Constructors are used to translate `Node`s to Swift values.
|
/// Constructors are used to translate `Node`s to Swift values.
|
||||||
public final class Constructor {
|
public final class Constructor {
|
||||||
/// Maps `Tag.Name`s to `Node.Scalar`s.
|
/// Maps `Tag.Name`s to `Node.Scalar`s.
|
||||||
|
@ -75,11 +73,7 @@ extension Constructor {
|
||||||
// JSON Schema
|
// JSON Schema
|
||||||
.bool: Bool.construct,
|
.bool: Bool.construct,
|
||||||
.float: Double.construct,
|
.float: Double.construct,
|
||||||
.null: NSNull.construct,
|
.int: MemoryLayout<Int>.size == 8 ? Int.construct : { Int.construct(from: $0) ?? Int64.construct(from: $0) }
|
||||||
.int: MemoryLayout<Int>.size == 8 ? Int.construct : { Int.construct(from: $0) ?? Int64.construct(from: $0) },
|
|
||||||
// http://yaml.org/type/index.html
|
|
||||||
.binary: Data.construct,
|
|
||||||
.timestamp: Date.construct
|
|
||||||
]
|
]
|
||||||
|
|
||||||
/// The default `Tag.Name` to `Node.Mapping` map.
|
/// The default `Tag.Name` to `Node.Mapping` map.
|
||||||
|
@ -115,19 +109,6 @@ public protocol ScalarConstructible {
|
||||||
static func construct(from scalar: Node.Scalar) -> Self?
|
static func construct(from scalar: Node.Scalar) -> Self?
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - ScalarConstructible UUID Conformance
|
|
||||||
|
|
||||||
extension UUID: ScalarConstructible {
|
|
||||||
/// Construct an instance of `UUID`, if possible, from the specified scalar.
|
|
||||||
///
|
|
||||||
/// - parameter scalar: The `Node.Scalar` from which to extract a value of type `UUID`, if possible.
|
|
||||||
///
|
|
||||||
/// - returns: An instance of `UUID`, if one was successfully extracted from the scalar.
|
|
||||||
public static func construct(from scalar: Node.Scalar) -> UUID? {
|
|
||||||
return UUID(uuidString: scalar.string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - ScalarConstructible Bool Conformance
|
// MARK: - ScalarConstructible Bool Conformance
|
||||||
|
|
||||||
extension Bool: ScalarConstructible {
|
extension Bool: ScalarConstructible {
|
||||||
|
@ -148,90 +129,6 @@ extension Bool: ScalarConstructible {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - ScalarConstructible Data Conformance
|
|
||||||
|
|
||||||
extension Data: ScalarConstructible {
|
|
||||||
/// Construct an instance of `Data`, if possible, from the specified scalar.
|
|
||||||
///
|
|
||||||
/// - parameter scalar: The `Node.Scalar` from which to extract a value of type `Data`, if possible.
|
|
||||||
///
|
|
||||||
/// - returns: An instance of `Data`, if one was successfully extracted from the scalar.
|
|
||||||
public static func construct(from scalar: Node.Scalar) -> Data? {
|
|
||||||
return Data(base64Encoded: scalar.string, options: .ignoreUnknownCharacters)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - ScalarConstructible Date Conformance
|
|
||||||
|
|
||||||
extension Date: ScalarConstructible {
|
|
||||||
/// Construct an instance of `Date`, if possible, from the specified scalar.
|
|
||||||
///
|
|
||||||
/// - parameter scalar: The `Node.Scalar` from which to extract a value of type `Date`, if possible.
|
|
||||||
///
|
|
||||||
/// - returns: An instance of `Date`, if one was successfully extracted from the scalar.
|
|
||||||
public static func construct(from scalar: Node.Scalar) -> Date? {
|
|
||||||
let range = NSRange(location: 0, length: scalar.string.utf16.count)
|
|
||||||
guard let result = timestampPattern.firstMatch(in: scalar.string, options: [], range: range),
|
|
||||||
result.range.location != NSNotFound else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
let components = (1..<result.numberOfRanges).map {
|
|
||||||
scalar.string.substring(with: result.range(at: $0))
|
|
||||||
}
|
|
||||||
|
|
||||||
var datecomponents = DateComponents()
|
|
||||||
datecomponents.calendar = gregorianCalendar
|
|
||||||
datecomponents.year = components[0].flatMap { Int($0) }
|
|
||||||
datecomponents.month = components[1].flatMap { Int($0) }
|
|
||||||
datecomponents.day = components[2].flatMap { Int($0) }
|
|
||||||
datecomponents.hour = components[3].flatMap { Int($0) }
|
|
||||||
datecomponents.minute = components[4].flatMap { Int($0) }
|
|
||||||
datecomponents.second = components[5].flatMap { Int($0) }
|
|
||||||
let nanoseconds: TimeInterval? = components[6].flatMap { fraction in
|
|
||||||
let length = fraction.count
|
|
||||||
let nanoseconds: Int?
|
|
||||||
if length < 9 {
|
|
||||||
nanoseconds = Int(fraction).map { number in
|
|
||||||
repeatElement(10, count: 9 - length).reduce(number, *)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nanoseconds = Int(fraction.prefix(9))
|
|
||||||
}
|
|
||||||
return nanoseconds.map { Double($0) / 1_000_000_000.0 }
|
|
||||||
}
|
|
||||||
datecomponents.timeZone = {
|
|
||||||
var seconds = 0
|
|
||||||
if let hourInSecond = components[9].flatMap({ Int($0) }).map({ $0 * 60 * 60 }) {
|
|
||||||
seconds += hourInSecond
|
|
||||||
}
|
|
||||||
if let minuteInSecond = components[10].flatMap({ Int($0) }).map({ $0 * 60 }) {
|
|
||||||
seconds += minuteInSecond
|
|
||||||
}
|
|
||||||
if components[8] == "-" { // sign
|
|
||||||
seconds *= -1
|
|
||||||
}
|
|
||||||
return TimeZone(secondsFromGMT: seconds)
|
|
||||||
}()
|
|
||||||
return datecomponents.date.map { nanoseconds.map($0.addingTimeInterval) ?? $0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
private static let gregorianCalendar = Calendar(identifier: .gregorian)
|
|
||||||
|
|
||||||
private static let timestampPattern: NSRegularExpression = pattern([
|
|
||||||
"^([0-9][0-9][0-9][0-9])", // year
|
|
||||||
"-([0-9][0-9]?)", // month
|
|
||||||
"-([0-9][0-9]?)", // day
|
|
||||||
"(?:(?:[Tt]|[ \\t]+)",
|
|
||||||
"([0-9][0-9]?)", // hour
|
|
||||||
":([0-9][0-9])", // minute
|
|
||||||
":([0-9][0-9])", // second
|
|
||||||
"(?:\\.([0-9]*))?", // fraction
|
|
||||||
"(?:[ \\t]*(Z|([-+])([0-9][0-9]?)", // tz_sign, tz_hour
|
|
||||||
"(?::([0-9][0-9]))?))?)?$" // tz_minute
|
|
||||||
].joined()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - ScalarConstructible Double Conformance
|
// MARK: - ScalarConstructible Double Conformance
|
||||||
extension Double: ScalarConstructible {}
|
extension Double: ScalarConstructible {}
|
||||||
// MARK: - ScalarConstructible Float Conformance
|
// MARK: - ScalarConstructible Float Conformance
|
||||||
|
@ -260,7 +157,7 @@ extension ScalarConstructible where Self: FloatingPoint & SexagesimalConvertible
|
||||||
case ".nan", ".NaN", ".NAN":
|
case ".nan", ".NaN", ".NAN":
|
||||||
return .nan
|
return .nan
|
||||||
default:
|
default:
|
||||||
let string = scalar.string.replacingOccurrences(of: "_", with: "")
|
let string = scalar.string.replacing("_", with: "")
|
||||||
if string.contains(":") {
|
if string.contains(":") {
|
||||||
return Self(sexagesimal: string)
|
return Self(sexagesimal: string)
|
||||||
}
|
}
|
||||||
|
@ -275,7 +172,7 @@ private extension FixedWidthInteger where Self: SexagesimalConvertible {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let scalarWithSign = scalar.string.replacingOccurrences(of: "_", with: "")
|
let scalarWithSign = scalar.string.replacing("_", with: "")
|
||||||
|
|
||||||
if scalarWithSign == "0" {
|
if scalarWithSign == "0" {
|
||||||
return 0
|
return 0
|
||||||
|
@ -387,24 +284,6 @@ extension String: ScalarConstructible {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Types that can't conform to ScalarConstructible
|
|
||||||
|
|
||||||
extension NSNull/*: ScalarConstructible*/ {
|
|
||||||
/// Construct an instance of `NSNull`, if possible, from the specified scalar.
|
|
||||||
///
|
|
||||||
/// - parameter scalar: The `Node.Scalar` from which to extract a value of type `NSNull`, if possible.
|
|
||||||
///
|
|
||||||
/// - returns: An instance of `NSNull`, if one was successfully extracted from the scalar.
|
|
||||||
public static func construct(from scalar: Node.Scalar) -> NSNull? {
|
|
||||||
switch scalar.string {
|
|
||||||
case "", "~", "null", "Null", "NULL":
|
|
||||||
return NSNull()
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Mapping
|
// MARK: Mapping
|
||||||
|
|
||||||
extension Dictionary {
|
extension Dictionary {
|
||||||
|
@ -484,19 +363,6 @@ extension Array {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension String {
|
|
||||||
func substring(with range: NSRange) -> Substring? {
|
|
||||||
guard range.location != NSNotFound else { return nil }
|
|
||||||
let utf16lowerBound = utf16.index(utf16.startIndex, offsetBy: range.location)
|
|
||||||
let utf16upperBound = utf16.index(utf16lowerBound, offsetBy: range.length)
|
|
||||||
guard let lowerBound = utf16lowerBound.samePosition(in: self),
|
|
||||||
let upperBound = utf16upperBound.samePosition(in: self) else {
|
|
||||||
fatalError("unreachable")
|
|
||||||
}
|
|
||||||
return self[lowerBound..<upperBound]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - SexagesimalConvertible
|
// MARK: - SexagesimalConvertible
|
||||||
|
|
||||||
/// Confirming types are convertible to base 60 numeric values.
|
/// Confirming types are convertible to base 60 numeric values.
|
||||||
|
@ -585,7 +451,7 @@ private extension String {
|
||||||
} else {
|
} else {
|
||||||
sign = 1
|
sign = 1
|
||||||
}
|
}
|
||||||
let digits = scalar.components(separatedBy: ":").compactMap(T.create).reversed()
|
let digits = scalar.split(separator: ":").map(String.init).compactMap(T.create).reversed()
|
||||||
let (_, value) = digits.reduce((1, 0) as (T, T)) { baseAndValue, digit in
|
let (_, value) = digits.reduce((1, 0) as (T, T)) { baseAndValue, digit in
|
||||||
let value = baseAndValue.1 + (digit * baseAndValue.0)
|
let value = baseAndValue.1 + (digit * baseAndValue.0)
|
||||||
let base = baseAndValue.0 * 60
|
let base = baseAndValue.0 * 60
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
// Copyright (c) 2017 Yams. All rights reserved.
|
// Copyright (c) 2017 Yams. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// `Codable`-style `Decoder` that can be used to decode a `Decodable` type from a given `String` and optional
|
/// `Codable`-style `Decoder` that can be used to decode a `Decodable` type from a given `String` and optional
|
||||||
/// user info mapping. Similar to `Foundation.JSONDecoder`.
|
/// user info mapping. Similar to `Foundation.JSONDecoder`.
|
||||||
public class YAMLDecoder {
|
public class YAMLDecoder {
|
||||||
|
@ -44,25 +42,6 @@ public class YAMLDecoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode a `Decodable` type from a given `Data` and optional user info mapping.
|
|
||||||
///
|
|
||||||
/// - parameter type: `Decodable` type to decode.
|
|
||||||
/// - parameter yaml: YAML data to decode.
|
|
||||||
/// - parameter userInfo: Additional key/values which can be used when looking up keys to decode.
|
|
||||||
///
|
|
||||||
/// - returns: Returns the decoded type `T`.
|
|
||||||
///
|
|
||||||
/// - throws: `DecodingError` or `YamlError` if something went wrong while decoding.
|
|
||||||
public func decode<T>(_ type: T.Type = T.self,
|
|
||||||
from yamlData: Data,
|
|
||||||
userInfo: [CodingUserInfoKey: Any] = [:]) throws -> T where T: Swift.Decodable {
|
|
||||||
guard let yamlString = String(data: yamlData, encoding: encoding.swiftStringEncoding) else {
|
|
||||||
throw YamlError.dataCouldNotBeDecoded(encoding: encoding.swiftStringEncoding)
|
|
||||||
}
|
|
||||||
|
|
||||||
return try decode(type, from: yamlString, userInfo: userInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encoding
|
/// Encoding
|
||||||
public var encoding: Parser.Encoding
|
public var encoding: Parser.Encoding
|
||||||
}
|
}
|
||||||
|
@ -234,7 +213,7 @@ extension _Decoder: SingleValueDecodingContainer {
|
||||||
|
|
||||||
// MARK: - Swift.SingleValueDecodingContainer Methods
|
// MARK: - Swift.SingleValueDecodingContainer Methods
|
||||||
|
|
||||||
func decodeNil() -> Bool { return node.null == NSNull() }
|
func decodeNil() -> Bool { return false }
|
||||||
func decode<T>(_ type: T.Type) throws -> T where T: Decodable & ScalarConstructible { return try construct(type) }
|
func decode<T>(_ type: T.Type) throws -> T where T: Decodable & ScalarConstructible { return try construct(type) }
|
||||||
func decode<T>(_ type: T.Type) throws -> T where T: Decodable {return try construct(type) ?? type.init(from: self) }
|
func decode<T>(_ type: T.Type) throws -> T where T: Decodable {return try construct(type) ?? type.init(from: self) }
|
||||||
|
|
||||||
|
@ -318,32 +297,6 @@ extension UInt16: ScalarConstructible {}
|
||||||
// MARK: - ScalarConstructible UInt32 Conformance
|
// MARK: - ScalarConstructible UInt32 Conformance
|
||||||
extension UInt32: ScalarConstructible {}
|
extension UInt32: ScalarConstructible {}
|
||||||
|
|
||||||
// MARK: - ScalarConstructible Decimal Conformance
|
|
||||||
|
|
||||||
extension Decimal: ScalarConstructible {
|
|
||||||
/// Construct an instance of `Decimal`, if possible, from the specified scalar.
|
|
||||||
///
|
|
||||||
/// - parameter scalar: The `Node.Scalar` from which to extract a value of type `Decimal`, if possible.
|
|
||||||
///
|
|
||||||
/// - returns: An instance of `Decimal`, if one was successfully extracted from the scalar.
|
|
||||||
public static func construct(from scalar: Node.Scalar) -> Decimal? {
|
|
||||||
return Decimal(string: scalar.string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - ScalarConstructible URL Conformance
|
|
||||||
|
|
||||||
extension URL: ScalarConstructible {
|
|
||||||
/// Construct an instance of `URL`, if possible, from the specified scalar.
|
|
||||||
///
|
|
||||||
/// - parameter scalar: The `Node.Scalar` from which to extract a value of type `URL`, if possible.
|
|
||||||
///
|
|
||||||
/// - returns: An instance of `URL`, if one was successfully extracted from the scalar.
|
|
||||||
public static func construct(from scalar: Node.Scalar) -> URL? {
|
|
||||||
return URL(string: scalar.string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Decoder.mark
|
// MARK: Decoder.mark
|
||||||
|
|
||||||
extension Decoder {
|
extension Decoder {
|
||||||
|
@ -352,17 +305,3 @@ extension Decoder {
|
||||||
return (self as? _Decoder)?.node.mark
|
return (self as? _Decoder)?.node.mark
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: TopLevelDecoder
|
|
||||||
|
|
||||||
#if canImport(Combine)
|
|
||||||
import protocol Combine.TopLevelDecoder
|
|
||||||
|
|
||||||
extension YAMLDecoder: TopLevelDecoder {
|
|
||||||
public typealias Input = Data
|
|
||||||
|
|
||||||
public func decode<T>(_ type: T.Type, from: Data) throws -> T where T: Decodable {
|
|
||||||
try decode(type, from: from, userInfo: [:])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#if SWIFT_PACKAGE
|
#if SWIFT_PACKAGE
|
||||||
@_implementationOnly import CYaml
|
@_implementationOnly import CYaml
|
||||||
#endif
|
#endif
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Produce a YAML string from objects.
|
/// Produce a YAML string from objects.
|
||||||
///
|
///
|
||||||
|
@ -161,7 +160,7 @@ public func serialize<Nodes>(
|
||||||
try emitter.open()
|
try emitter.open()
|
||||||
try nodes.forEach(emitter.serialize)
|
try nodes.forEach(emitter.serialize)
|
||||||
try emitter.close()
|
try emitter.close()
|
||||||
return String(data: emitter.data, encoding: .utf8)!
|
return emitter.data
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produce a YAML string from a `Node`.
|
/// Produce a YAML string from a `Node`.
|
||||||
|
@ -223,8 +222,8 @@ public final class Emitter {
|
||||||
case crln
|
case crln
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve this Emitter's binary output.
|
/// Retrieve this Emitter's string output.
|
||||||
public internal(set) var data = Data()
|
public internal(set) var data = ""
|
||||||
|
|
||||||
/// Configuration options to use when emitting YAML.
|
/// Configuration options to use when emitting YAML.
|
||||||
public struct Options {
|
public struct Options {
|
||||||
|
@ -304,7 +303,7 @@ public final class Emitter {
|
||||||
yaml_emitter_set_output(&self.emitter, { pointer, buffer, size in
|
yaml_emitter_set_output(&self.emitter, { pointer, buffer, size in
|
||||||
guard let buffer = buffer else { return 0 }
|
guard let buffer = buffer else { return 0 }
|
||||||
let emitter = unsafeBitCast(pointer, to: Emitter.self)
|
let emitter = unsafeBitCast(pointer, to: Emitter.self)
|
||||||
emitter.data.append(buffer, count: size)
|
emitter.data.append(String(cString: buffer))
|
||||||
return 1
|
return 1
|
||||||
}, unsafeBitCast(self, to: UnsafeMutableRawPointer.self))
|
}, unsafeBitCast(self, to: UnsafeMutableRawPointer.self))
|
||||||
|
|
||||||
|
|
|
@ -26,14 +26,6 @@ extension Mark: CustomStringConvertible {
|
||||||
extension Mark {
|
extension Mark {
|
||||||
/// Returns snippet string pointed by Mark instance from YAML String.
|
/// Returns snippet string pointed by Mark instance from YAML String.
|
||||||
public func snippet(from yaml: String) -> String {
|
public func snippet(from yaml: String) -> String {
|
||||||
let contents = yaml.substring(at: line - 1)
|
fatalError("Unimplemented")
|
||||||
let columnIndex = contents.unicodeScalars
|
|
||||||
.index(contents.unicodeScalars.startIndex,
|
|
||||||
offsetBy: column - 1,
|
|
||||||
limitedBy: contents.unicodeScalars.endIndex)?
|
|
||||||
.samePosition(in: contents.utf16) ?? contents.utf16.endIndex
|
|
||||||
let columnInUTF16 = contents.utf16.distance(from: contents.utf16.startIndex, to: columnIndex)
|
|
||||||
return contents.endingWithNewLine +
|
|
||||||
String(repeating: " ", count: columnInUTF16) + "^"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
// Copyright (c) 2016 Yams. All rights reserved.
|
// Copyright (c) 2016 Yams. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// YAML Node.
|
/// YAML Node.
|
||||||
public enum Node: Hashable {
|
public enum Node: Hashable {
|
||||||
/// Scalar node.
|
/// Scalar node.
|
||||||
|
@ -92,31 +90,11 @@ extension Node {
|
||||||
return scalar.flatMap(Double.construct)
|
return scalar.flatMap(Double.construct)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This node as an `NSNull`, if convertible.
|
|
||||||
public var null: NSNull? {
|
|
||||||
return scalar.flatMap(NSNull.construct)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This node as an `Int`, if convertible.
|
/// This node as an `Int`, if convertible.
|
||||||
public var int: Int? {
|
public var int: Int? {
|
||||||
return scalar.flatMap(Int.construct)
|
return scalar.flatMap(Int.construct)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This node as a `Data`, if convertible.
|
|
||||||
public var binary: Data? {
|
|
||||||
return scalar.flatMap(Data.construct)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This node as a `Date`, if convertible.
|
|
||||||
public var timestamp: Date? {
|
|
||||||
return scalar.flatMap(Date.construct)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This node as a `UUID`, if convertible.
|
|
||||||
public var uuid: UUID? {
|
|
||||||
return scalar.flatMap(UUID.construct)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Typed accessor methods
|
// MARK: Typed accessor methods
|
||||||
|
|
||||||
/// Returns this node mapped as an `Array<Node>`. If the node isn't a `Node.sequence`, the array will be
|
/// Returns this node mapped as an `Array<Node>`. If the node isn't a `Node.sequence`, the array will be
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#if SWIFT_PACKAGE
|
#if SWIFT_PACKAGE
|
||||||
@_implementationOnly import CYaml
|
@_implementationOnly import CYaml
|
||||||
#endif
|
#endif
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Parse all YAML documents in a String
|
/// Parse all YAML documents in a String
|
||||||
/// and produce corresponding Swift objects.
|
/// and produce corresponding Swift objects.
|
||||||
|
@ -127,25 +126,8 @@ public final class Parser {
|
||||||
/// This value is case insensitive.
|
/// This value is case insensitive.
|
||||||
public static var `default`: Encoding = {
|
public static var `default`: Encoding = {
|
||||||
let key = "YAMS_DEFAULT_ENCODING"
|
let key = "YAMS_DEFAULT_ENCODING"
|
||||||
if let yamsEncoding = ProcessInfo.processInfo.environment[key],
|
|
||||||
let encoding = Encoding(rawValue: yamsEncoding.lowercased()) {
|
|
||||||
print("""
|
|
||||||
`Parser.Encoding.default` was set to `\(encoding)` by the `\(key)` environment variable.
|
|
||||||
""")
|
|
||||||
return encoding
|
|
||||||
}
|
|
||||||
return key.utf8.withContiguousStorageIfAvailable({ _ in true }) != nil ? .utf8 : .utf16
|
return key.utf8.withContiguousStorageIfAvailable({ _ in true }) != nil ? .utf8 : .utf16
|
||||||
}()
|
}()
|
||||||
|
|
||||||
/// The equivalent `Swift.Encoding` value for `self`.
|
|
||||||
internal var swiftStringEncoding: String.Encoding {
|
|
||||||
switch self {
|
|
||||||
case .utf8:
|
|
||||||
return .utf8
|
|
||||||
case .utf16:
|
|
||||||
return .utf16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Encoding
|
/// Encoding
|
||||||
public let encoding: Encoding
|
public let encoding: Encoding
|
||||||
|
@ -182,40 +164,10 @@ public final class Parser {
|
||||||
try utf8Slice.withUnsafeBytes(startParse(with:))
|
try utf8Slice.withUnsafeBytes(startParse(with:))
|
||||||
}
|
}
|
||||||
case .utf16:
|
case .utf16:
|
||||||
// use native endianness
|
fatalError("Unimplemented")
|
||||||
let isLittleEndian = 1 == 1.littleEndian
|
|
||||||
yaml_parser_set_encoding(&parser, isLittleEndian ? YAML_UTF16LE_ENCODING : YAML_UTF16BE_ENCODING)
|
|
||||||
let encoding: String.Encoding = isLittleEndian ? .utf16LittleEndian : .utf16BigEndian
|
|
||||||
let data = yaml.data(using: encoding)!
|
|
||||||
buffer = .utf16(data)
|
|
||||||
try data.withUnsafeBytes(startParse(with:))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set up a `Parser` with a `Data` value as input.
|
|
||||||
///
|
|
||||||
/// - parameter string: YAML Data encoded using the `encoding` encoding.
|
|
||||||
/// - parameter resolver: Resolver, `.default` if omitted.
|
|
||||||
/// - parameter constructor: Constructor, `.default` if omitted.
|
|
||||||
/// - parameter encoding: Encoding, `.default` if omitted.
|
|
||||||
///
|
|
||||||
/// - throws: `YamlError`.
|
|
||||||
public convenience init(yaml data: Data,
|
|
||||||
resolver: Resolver = .default,
|
|
||||||
constructor: Constructor = .default,
|
|
||||||
encoding: Encoding = .default) throws {
|
|
||||||
guard let yamlString = String(data: data, encoding: encoding.swiftStringEncoding) else {
|
|
||||||
throw YamlError.dataCouldNotBeDecoded(encoding: encoding.swiftStringEncoding)
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.init(
|
|
||||||
yaml: yamlString,
|
|
||||||
resolver: resolver,
|
|
||||||
constructor: constructor,
|
|
||||||
encoding: encoding
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
yaml_parser_delete(&parser)
|
yaml_parser_delete(&parser)
|
||||||
}
|
}
|
||||||
|
@ -258,7 +210,6 @@ public final class Parser {
|
||||||
private enum Buffer {
|
private enum Buffer {
|
||||||
case utf8View(String.UTF8View)
|
case utf8View(String.UTF8View)
|
||||||
case utf8Slice(ArraySlice<CChar>)
|
case utf8Slice(ArraySlice<CChar>)
|
||||||
case utf16(Data)
|
|
||||||
}
|
}
|
||||||
private var buffer: Buffer
|
private var buffer: Buffer
|
||||||
}
|
}
|
||||||
|
@ -395,11 +346,7 @@ private class Event {
|
||||||
return string(from: event.data.scalar.tag)
|
return string(from: event.data.scalar.tag)
|
||||||
}
|
}
|
||||||
var scalarValue: String {
|
var scalarValue: String {
|
||||||
// scalar may contain NULL characters
|
return String(cString: event.data.scalar.value)
|
||||||
let buffer = UnsafeBufferPointer(start: event.data.scalar.value,
|
|
||||||
count: event.data.scalar.length)
|
|
||||||
// libYAML converts scalar characters into UTF8 if input is other than YAML_UTF8_ENCODING
|
|
||||||
return String(bytes: buffer, encoding: .utf8)!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sequence
|
// sequence
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
// Copyright (c) 2017 Yams. All rights reserved.
|
// Copyright (c) 2017 Yams. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
#if os(iOS) || os(macOS) || os(watchOS) || os(tvOS)
|
#if os(iOS) || os(macOS) || os(watchOS) || os(tvOS)
|
||||||
import Darwin
|
import Darwin
|
||||||
private let cpow: (_: Double, _: Double) -> Double = Darwin.pow
|
private let cpow: (_: Double, _: Double) -> Double = Darwin.pow
|
||||||
|
@ -82,13 +80,6 @@ extension ScalarRepresentable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NSNull: ScalarRepresentable {
|
|
||||||
/// This value's `Node.scalar` representation.
|
|
||||||
public func represented() -> Node.Scalar {
|
|
||||||
return .init("null", Tag(.null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Bool: ScalarRepresentable {
|
extension Bool: ScalarRepresentable {
|
||||||
/// This value's `Node.scalar` representation.
|
/// This value's `Node.scalar` representation.
|
||||||
public func represented() -> Node.Scalar {
|
public func represented() -> Node.Scalar {
|
||||||
|
@ -96,106 +87,36 @@ extension Bool: ScalarRepresentable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Data: ScalarRepresentable {
|
|
||||||
/// This value's `Node.scalar` representation.
|
|
||||||
public func represented() -> Node.Scalar {
|
|
||||||
return .init(base64EncodedString(), Tag(.binary))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Date: ScalarRepresentable {
|
|
||||||
/// This value's `Node.scalar` representation.
|
|
||||||
public func represented() -> Node.Scalar {
|
|
||||||
return .init(iso8601String, Tag(.timestamp))
|
|
||||||
}
|
|
||||||
|
|
||||||
private var iso8601String: String {
|
|
||||||
let (integral, millisecond) = timeIntervalSinceReferenceDate.separateFractionalSecond(withPrecision: 3)
|
|
||||||
guard millisecond != 0 else { return iso8601Formatter.string(from: self) }
|
|
||||||
|
|
||||||
let dateWithoutMillisecond = Date(timeIntervalSinceReferenceDate: integral)
|
|
||||||
return iso8601WithoutZFormatter.string(from: dateWithoutMillisecond) +
|
|
||||||
String(format: ".%03d", millisecond).trimmingCharacters(in: characterSetZero) + "Z"
|
|
||||||
}
|
|
||||||
|
|
||||||
private var iso8601StringWithFullNanosecond: String {
|
|
||||||
let (integral, nanosecond) = timeIntervalSinceReferenceDate.separateFractionalSecond(withPrecision: 9)
|
|
||||||
guard nanosecond != 0 else { return iso8601Formatter.string(from: self) }
|
|
||||||
|
|
||||||
let dateWithoutNanosecond = Date(timeIntervalSinceReferenceDate: integral)
|
|
||||||
return iso8601WithoutZFormatter.string(from: dateWithoutNanosecond) +
|
|
||||||
String(format: ".%09d", nanosecond).trimmingCharacters(in: characterSetZero) + "Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension TimeInterval {
|
|
||||||
/// Separates the time interval into integral and fractional components, then rounds the `fractional`
|
|
||||||
/// component to `precision` number of digits.
|
|
||||||
///
|
|
||||||
/// - returns: Tuple of integral part and converted fractional part
|
|
||||||
func separateFractionalSecond(withPrecision precision: Int) -> (integral: TimeInterval, fractional: Int) {
|
|
||||||
var integral = 0.0
|
|
||||||
let fractional = modf(self, &integral)
|
|
||||||
|
|
||||||
// TODO(TF-1203): Can't use `pow` free function due to
|
|
||||||
// https://bugs.swift.org/browse/TF-1203.
|
|
||||||
let radix = cpow(10.0, Double(precision))
|
|
||||||
|
|
||||||
let rounded = Int((fractional * radix).rounded())
|
|
||||||
let quotient = rounded / Int(radix)
|
|
||||||
return quotient != 0 ? // carry-up?
|
|
||||||
(integral + TimeInterval(quotient), rounded % Int(radix)) :
|
|
||||||
(integral, rounded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private let characterSetZero = CharacterSet(charactersIn: "0")
|
|
||||||
|
|
||||||
private let iso8601Formatter: DateFormatter = {
|
|
||||||
var formatter = DateFormatter()
|
|
||||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
|
||||||
formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
|
|
||||||
formatter.timeZone = TimeZone(secondsFromGMT: 0)
|
|
||||||
return formatter
|
|
||||||
}()
|
|
||||||
|
|
||||||
private let iso8601WithoutZFormatter: DateFormatter = {
|
|
||||||
var formatter = DateFormatter()
|
|
||||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
|
||||||
formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss"
|
|
||||||
formatter.timeZone = TimeZone(secondsFromGMT: 0)
|
|
||||||
return formatter
|
|
||||||
}()
|
|
||||||
|
|
||||||
extension Double: ScalarRepresentable {
|
extension Double: ScalarRepresentable {
|
||||||
/// This value's `Node.scalar` representation.
|
/// This value's `Node.scalar` representation.
|
||||||
public func represented() -> Node.Scalar {
|
public func represented() -> Node.Scalar {
|
||||||
return .init(doubleFormatter.string(for: self)!.replacingOccurrences(of: "+-", with: "-"), Tag(.float))
|
if #available(macOS 13.0, *) {
|
||||||
|
return .init(doubleFormatter.string(for: self).replacing("+-", with: "-"), Tag(.float))
|
||||||
|
} else {
|
||||||
|
fatalError("Unimplemented")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Float: ScalarRepresentable {
|
extension Float: ScalarRepresentable {
|
||||||
/// This value's `Node.scalar` representation.
|
/// This value's `Node.scalar` representation.
|
||||||
public func represented() -> Node.Scalar {
|
public func represented() -> Node.Scalar {
|
||||||
return .init(floatFormatter.string(for: self)!.replacingOccurrences(of: "+-", with: "-"), Tag(.float))
|
if #available(macOS 13.0, *) {
|
||||||
|
return .init(floatFormatter.string(for: self).replacing("+-", with: "-"), Tag(.float))
|
||||||
|
} else {
|
||||||
|
fatalError("Unimplemented")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func numberFormatter(with significantDigits: Int) -> NumberFormatter {
|
private let doubleFormatter = YamsNumberFormatter()
|
||||||
let formatter = NumberFormatter()
|
private let floatFormatter = YamsNumberFormatter()
|
||||||
formatter.locale = Locale(identifier: "en_US")
|
|
||||||
formatter.numberStyle = .scientific
|
|
||||||
formatter.usesSignificantDigits = true
|
|
||||||
formatter.maximumSignificantDigits = significantDigits
|
|
||||||
formatter.positiveInfinitySymbol = ".inf"
|
|
||||||
formatter.negativeInfinitySymbol = "-.inf"
|
|
||||||
formatter.notANumberSymbol = ".nan"
|
|
||||||
formatter.exponentSymbol = "e+"
|
|
||||||
return formatter
|
|
||||||
}
|
|
||||||
|
|
||||||
private let doubleFormatter = numberFormatter(with: 15)
|
private struct YamsNumberFormatter {
|
||||||
private let floatFormatter = numberFormatter(with: 7)
|
func string(for number: some FloatingPoint) -> String {
|
||||||
|
"\(number)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Support `Float80`
|
// TODO: Support `Float80`
|
||||||
//extension Float80: ScalarRepresentable {}
|
//extension Float80: ScalarRepresentable {}
|
||||||
|
@ -230,20 +151,6 @@ extension Optional: NodeRepresentable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Decimal: ScalarRepresentable {
|
|
||||||
/// This value's `Node.scalar` representation.
|
|
||||||
public func represented() -> Node.Scalar {
|
|
||||||
return .init(description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension URL: ScalarRepresentable {
|
|
||||||
/// This value's `Node.scalar` representation.
|
|
||||||
public func represented() -> Node.Scalar {
|
|
||||||
return .init(absoluteString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension String: ScalarRepresentable {
|
extension String: ScalarRepresentable {
|
||||||
/// This value's `Node.scalar` representation.
|
/// This value's `Node.scalar` representation.
|
||||||
public func represented() -> Node.Scalar {
|
public func represented() -> Node.Scalar {
|
||||||
|
@ -252,13 +159,6 @@ extension String: ScalarRepresentable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UUID: ScalarRepresentable {
|
|
||||||
/// This value's `Node.scalar` representation.
|
|
||||||
public func represented() -> Node.Scalar {
|
|
||||||
return .init(uuidString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - ScalarRepresentableCustomizedForCodable
|
// MARK: - ScalarRepresentableCustomizedForCodable
|
||||||
|
|
||||||
/// Types conforming to this protocol can be encoded by `YamlEncoder`.
|
/// Types conforming to this protocol can be encoded by `YamlEncoder`.
|
||||||
|
@ -275,8 +175,6 @@ extension YAMLEncodable where Self: ScalarRepresentable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Bool: YAMLEncodable {}
|
extension Bool: YAMLEncodable {}
|
||||||
extension Data: YAMLEncodable {}
|
|
||||||
extension Decimal: YAMLEncodable {}
|
|
||||||
extension Int: YAMLEncodable {}
|
extension Int: YAMLEncodable {}
|
||||||
extension Int8: YAMLEncodable {}
|
extension Int8: YAMLEncodable {}
|
||||||
extension Int16: YAMLEncodable {}
|
extension Int16: YAMLEncodable {}
|
||||||
|
@ -287,41 +185,18 @@ extension UInt8: YAMLEncodable {}
|
||||||
extension UInt16: YAMLEncodable {}
|
extension UInt16: YAMLEncodable {}
|
||||||
extension UInt32: YAMLEncodable {}
|
extension UInt32: YAMLEncodable {}
|
||||||
extension UInt64: YAMLEncodable {}
|
extension UInt64: YAMLEncodable {}
|
||||||
extension URL: YAMLEncodable {}
|
|
||||||
extension String: YAMLEncodable {}
|
extension String: YAMLEncodable {}
|
||||||
extension UUID: YAMLEncodable {}
|
|
||||||
|
|
||||||
extension Date: YAMLEncodable {
|
|
||||||
/// Returns this value wrapped in a `Node.scalar`.
|
|
||||||
public func box() -> Node {
|
|
||||||
return Node(iso8601StringWithFullNanosecond, Tag(.timestamp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Double: YAMLEncodable {
|
extension Double: YAMLEncodable {
|
||||||
/// Returns this value wrapped in a `Node.scalar`.
|
/// Returns this value wrapped in a `Node.scalar`.
|
||||||
public func box() -> Node {
|
public func box() -> Node {
|
||||||
return Node(formattedStringForCodable, Tag(.float))
|
return Node("\(self)", Tag(.float))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Float: YAMLEncodable {
|
extension Float: YAMLEncodable {
|
||||||
/// Returns this value wrapped in a `Node.scalar`.
|
/// Returns this value wrapped in a `Node.scalar`.
|
||||||
public func box() -> Node {
|
public func box() -> Node {
|
||||||
return Node(formattedStringForCodable, Tag(.float))
|
return Node("\(self)", Tag(.float))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension FloatingPoint where Self: CVarArg {
|
|
||||||
var formattedStringForCodable: String {
|
|
||||||
// Since `NumberFormatter` creates a string with insufficient precision for Decode,
|
|
||||||
// it uses with `String(format:...)`
|
|
||||||
let string = String(format: "%.*g", DBL_DECIMAL_DIG, self)
|
|
||||||
// "%*.g" does not use scientific notation if the exponent is less than –4.
|
|
||||||
// So fallback to using `NumberFormatter` if string does not uses scientific notation.
|
|
||||||
guard string.lazy.suffix(5).contains("e") else {
|
|
||||||
return doubleFormatter.string(for: self)!.replacingOccurrences(of: "+-", with: "-")
|
|
||||||
}
|
|
||||||
return string
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,17 +6,14 @@
|
||||||
// Copyright (c) 2016 Yams. All rights reserved.
|
// Copyright (c) 2016 Yams. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Class used to resolve nodes to tags based on customizable rules.
|
/// Class used to resolve nodes to tags based on customizable rules.
|
||||||
public final class Resolver {
|
public final class Resolver {
|
||||||
/// Rule describing how to resolve tags from regex patterns.
|
/// Rule describing how to resolve tags from regex patterns.
|
||||||
public struct Rule {
|
public struct Rule {
|
||||||
/// The tag name this rule applies to.
|
/// The tag name this rule applies to.
|
||||||
public let tag: Tag.Name
|
public let tag: Tag.Name
|
||||||
fileprivate let regexp: NSRegularExpression
|
private let _matches: (String) -> Bool
|
||||||
/// The regex pattern used to resolve this rule.
|
func matches(in string: String) -> Bool { _matches(string) }
|
||||||
public var pattern: String { return regexp.pattern }
|
|
||||||
|
|
||||||
/// Create a rule with the specified tag name and regex pattern.
|
/// Create a rule with the specified tag name and regex pattern.
|
||||||
///
|
///
|
||||||
|
@ -26,7 +23,12 @@ public final class Resolver {
|
||||||
/// - throws: Throws an error if the regular expression pattern is invalid.
|
/// - throws: Throws an error if the regular expression pattern is invalid.
|
||||||
public init(_ tag: Tag.Name, _ pattern: String) throws {
|
public init(_ tag: Tag.Name, _ pattern: String) throws {
|
||||||
self.tag = tag
|
self.tag = tag
|
||||||
self.regexp = try .init(pattern: pattern, options: [])
|
if #available(macOS 13.0, *) {
|
||||||
|
let regex = try Regex(pattern)
|
||||||
|
_matches = { $0.contains(regex) }
|
||||||
|
} else {
|
||||||
|
fatalError("Unimplemented")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +85,7 @@ public final class Resolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveTag(from string: String) -> Tag.Name {
|
func resolveTag(from string: String) -> Tag.Name {
|
||||||
for rule in rules where rule.regexp.matches(in: string) {
|
for rule in rules where rule.matches(in: string) {
|
||||||
return rule.tag
|
return rule.tag
|
||||||
}
|
}
|
||||||
return .str
|
return .str
|
||||||
|
@ -153,21 +155,3 @@ extension Resolver.Rule {
|
||||||
|
|
||||||
// swiftlint:enable force_try
|
// swiftlint:enable force_try
|
||||||
}
|
}
|
||||||
|
|
||||||
func pattern(_ string: String) -> NSRegularExpression {
|
|
||||||
do {
|
|
||||||
return try .init(pattern: string, options: [])
|
|
||||||
} catch {
|
|
||||||
fatalError("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension NSRegularExpression {
|
|
||||||
func matches(in string: String) -> Bool {
|
|
||||||
let range = NSRange(location: 0, length: string.utf16.count)
|
|
||||||
if let match = firstMatch(in: string, options: [], range: range) {
|
|
||||||
return match.range.location != NSNotFound
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
//
|
|
||||||
// String+Yams.swift
|
|
||||||
// Yams
|
|
||||||
//
|
|
||||||
// Created by Norio Nomura on 12/7/16.
|
|
||||||
// Copyright (c) 2016 Yams. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension String {
|
|
||||||
typealias LineNumberColumnAndContents = (lineNumber: Int, column: Int, contents: String)
|
|
||||||
|
|
||||||
/// line number, column and contents at offset.
|
|
||||||
///
|
|
||||||
/// - parameter offset: Int
|
|
||||||
///
|
|
||||||
/// - returns: lineNumber: line number start from 0,
|
|
||||||
/// column: utf16 column start from 0,
|
|
||||||
/// contents: substring of line
|
|
||||||
func lineNumberColumnAndContents(at offset: Int) -> LineNumberColumnAndContents? {
|
|
||||||
return index(startIndex, offsetBy: offset, limitedBy: endIndex).flatMap(lineNumberColumnAndContents)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// line number, column and contents at Index.
|
|
||||||
///
|
|
||||||
/// - parameter index: String.Index
|
|
||||||
///
|
|
||||||
/// - returns: lineNumber: line number start from 0,
|
|
||||||
/// column: utf16 column start from 0,
|
|
||||||
/// contents: substring of line
|
|
||||||
func lineNumberColumnAndContents(at index: Index) -> LineNumberColumnAndContents {
|
|
||||||
assert((startIndex..<endIndex).contains(index))
|
|
||||||
var number = 0
|
|
||||||
var outStartIndex = startIndex, outEndIndex = startIndex, outContentsEndIndex = startIndex
|
|
||||||
getLineStart(&outStartIndex, end: &outEndIndex, contentsEnd: &outContentsEndIndex,
|
|
||||||
for: startIndex..<startIndex)
|
|
||||||
while outEndIndex <= index && outEndIndex < endIndex {
|
|
||||||
number += 1
|
|
||||||
let range: Range = outEndIndex..<outEndIndex
|
|
||||||
getLineStart(&outStartIndex, end: &outEndIndex, contentsEnd: &outContentsEndIndex,
|
|
||||||
for: range)
|
|
||||||
}
|
|
||||||
let utf16StartIndex = outStartIndex.samePosition(in: utf16)!
|
|
||||||
let utf16Index = index.samePosition(in: utf16)!
|
|
||||||
return (
|
|
||||||
number,
|
|
||||||
utf16.distance(from: utf16StartIndex, to: utf16Index),
|
|
||||||
String(self[outStartIndex..<outEndIndex])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// substring indicated by line number.
|
|
||||||
///
|
|
||||||
/// - parameter line: line number starts from 0.
|
|
||||||
///
|
|
||||||
/// - returns: substring of line contains line ending characters
|
|
||||||
func substring(at line: Int) -> String {
|
|
||||||
var number = 0
|
|
||||||
var outStartIndex = startIndex, outEndIndex = startIndex, outContentsEndIndex = startIndex
|
|
||||||
getLineStart(&outStartIndex, end: &outEndIndex, contentsEnd: &outContentsEndIndex,
|
|
||||||
for: startIndex..<startIndex)
|
|
||||||
while number < line && outEndIndex < endIndex {
|
|
||||||
number += 1
|
|
||||||
let range: Range = outEndIndex..<outEndIndex
|
|
||||||
getLineStart(&outStartIndex, end: &outEndIndex, contentsEnd: &outContentsEndIndex,
|
|
||||||
for: range)
|
|
||||||
}
|
|
||||||
return String(self[outStartIndex..<outEndIndex])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// String appending newline if is not ending with newline.
|
|
||||||
var endingWithNewLine: String {
|
|
||||||
let isEndsWithNewLines = unicodeScalars.last.map(CharacterSet.newlines.contains) ?? false
|
|
||||||
if isEndsWithNewLines {
|
|
||||||
return self
|
|
||||||
} else {
|
|
||||||
return self + "\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,7 +9,6 @@
|
||||||
#if SWIFT_PACKAGE
|
#if SWIFT_PACKAGE
|
||||||
@_implementationOnly import CYaml
|
@_implementationOnly import CYaml
|
||||||
#endif
|
#endif
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Errors thrown by Yams APIs.
|
/// Errors thrown by Yams APIs.
|
||||||
public enum YamlError: Error {
|
public enum YamlError: Error {
|
||||||
|
@ -71,9 +70,7 @@ public enum YamlError: Error {
|
||||||
case representer(problem: String)
|
case representer(problem: String)
|
||||||
|
|
||||||
/// String data could not be decoded with the specified encoding.
|
/// String data could not be decoded with the specified encoding.
|
||||||
///
|
case dataCouldNotBeDecoded
|
||||||
/// - parameter encoding: The string encoding used to decode the string data.
|
|
||||||
case dataCouldNotBeDecoded(encoding: String.Encoding)
|
|
||||||
|
|
||||||
/// The error context.
|
/// The error context.
|
||||||
public struct Context: CustomStringConvertible {
|
public struct Context: CustomStringConvertible {
|
||||||
|
@ -158,13 +155,8 @@ extension YamlError: CustomStringConvertible {
|
||||||
return "No error is produced"
|
return "No error is produced"
|
||||||
case .memory:
|
case .memory:
|
||||||
return "Memory error"
|
return "Memory error"
|
||||||
case let .reader(problem, offset, value, yaml):
|
case let .reader(problem, offset, value, _):
|
||||||
guard let (line, column, contents) = offset.flatMap(yaml.lineNumberColumnAndContents(at:)) else {
|
return "\(problem) at offset: \(String(describing: offset)), value: \(value)"
|
||||||
return "\(problem) at offset: \(String(describing: offset)), value: \(value)"
|
|
||||||
}
|
|
||||||
let mark = Mark(line: line + 1, column: column + 1)
|
|
||||||
return "\(mark): error: reader: \(problem):\n" + contents.endingWithNewLine
|
|
||||||
+ String(repeating: " ", count: column) + "^"
|
|
||||||
case let .scanner(context, problem, mark, yaml):
|
case let .scanner(context, problem, mark, yaml):
|
||||||
return "\(mark): error: scanner: \(context?.description ?? "")\(problem):\n" + mark.snippet(from: yaml)
|
return "\(mark): error: scanner: \(context?.description ?? "")\(problem):\n" + mark.snippet(from: yaml)
|
||||||
case let .parser(context, problem, mark, yaml):
|
case let .parser(context, problem, mark, yaml):
|
||||||
|
@ -173,8 +165,8 @@ extension YamlError: CustomStringConvertible {
|
||||||
return "\(mark): error: composer: \(context?.description ?? "")\(problem):\n" + mark.snippet(from: yaml)
|
return "\(mark): error: composer: \(context?.description ?? "")\(problem):\n" + mark.snippet(from: yaml)
|
||||||
case let .writer(problem), let .emitter(problem), let .representer(problem):
|
case let .writer(problem), let .emitter(problem), let .representer(problem):
|
||||||
return problem
|
return problem
|
||||||
case .dataCouldNotBeDecoded(encoding: let encoding):
|
case .dataCouldNotBeDecoded:
|
||||||
return "String could not be decoded from data using '\(encoding)' encoding"
|
return "String could not be decoded from data"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,43 @@
|
||||||
import Yams; print(try Yams.load(yaml: "a: 0")!)
|
import Yams
|
||||||
|
|
||||||
|
let yaml = """
|
||||||
|
a: 0
|
||||||
|
b: 1.2
|
||||||
|
c: [1, 2, 3]
|
||||||
|
d:
|
||||||
|
- a
|
||||||
|
- b
|
||||||
|
- c
|
||||||
|
"""
|
||||||
|
|
||||||
|
dump(try Yams.load(yaml: yaml)!)
|
||||||
|
|
||||||
|
enum Instrument: String, Codable {
|
||||||
|
case tenorSaxophone = "Tenor Saxophone"
|
||||||
|
case trumpet = "Trumpet"
|
||||||
|
}
|
||||||
|
|
||||||
|
let bandYAML = """
|
||||||
|
members:
|
||||||
|
- name: John Coltrane
|
||||||
|
age: 27
|
||||||
|
- name: Miles Davis
|
||||||
|
age: 23
|
||||||
|
instrument: Trumpet
|
||||||
|
"""
|
||||||
|
|
||||||
|
struct Person: Codable {
|
||||||
|
let name: String
|
||||||
|
let age: Int
|
||||||
|
let instrument: Instrument?
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Band: Codable {
|
||||||
|
let members: [Person]
|
||||||
|
}
|
||||||
|
|
||||||
|
let band = try YAMLDecoder().decode(Band.self, from: bandYAML)
|
||||||
|
dump(band)
|
||||||
|
|
||||||
|
print("Back to yaml:")
|
||||||
|
print(try YAMLEncoder().encode(band))
|
||||||
|
|
Loading…
Reference in New Issue