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
|
||||
test --test_output=errors
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// swift-tools-version:5.4
|
||||
// swift-tools-version:5.7
|
||||
import PackageDescription
|
||||
|
||||
let dependencies: [Package.Dependency]
|
||||
|
@ -10,6 +10,7 @@ dependencies = []
|
|||
|
||||
let package = Package(
|
||||
name: "Yams",
|
||||
platforms: [.macOS(.v13)],
|
||||
products: [
|
||||
.library(name: "Yams", targets: ["Yams"]),
|
||||
.executable(name: "yams-cli", targets: ["yams-cli"])
|
||||
|
@ -29,14 +30,6 @@ let package = Package(
|
|||
name: "Yams",
|
||||
dependencies: ["CYaml"],
|
||||
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.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Constructors are used to translate `Node`s to Swift values.
|
||||
public final class Constructor {
|
||||
/// Maps `Tag.Name`s to `Node.Scalar`s.
|
||||
|
@ -75,11 +73,7 @@ extension Constructor {
|
|||
// JSON Schema
|
||||
.bool: Bool.construct,
|
||||
.float: Double.construct,
|
||||
.null: NSNull.construct,
|
||||
.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
|
||||
.int: MemoryLayout<Int>.size == 8 ? Int.construct : { Int.construct(from: $0) ?? Int64.construct(from: $0) }
|
||||
]
|
||||
|
||||
/// The default `Tag.Name` to `Node.Mapping` map.
|
||||
|
@ -115,19 +109,6 @@ public protocol ScalarConstructible {
|
|||
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
|
||||
|
||||
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
|
||||
extension Double: ScalarConstructible {}
|
||||
// MARK: - ScalarConstructible Float Conformance
|
||||
|
@ -260,7 +157,7 @@ extension ScalarConstructible where Self: FloatingPoint & SexagesimalConvertible
|
|||
case ".nan", ".NaN", ".NAN":
|
||||
return .nan
|
||||
default:
|
||||
let string = scalar.string.replacingOccurrences(of: "_", with: "")
|
||||
let string = scalar.string.replacing("_", with: "")
|
||||
if string.contains(":") {
|
||||
return Self(sexagesimal: string)
|
||||
}
|
||||
|
@ -275,7 +172,7 @@ private extension FixedWidthInteger where Self: SexagesimalConvertible {
|
|||
return nil
|
||||
}
|
||||
|
||||
let scalarWithSign = scalar.string.replacingOccurrences(of: "_", with: "")
|
||||
let scalarWithSign = scalar.string.replacing("_", with: "")
|
||||
|
||||
if scalarWithSign == "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
|
||||
|
||||
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
|
||||
|
||||
/// Confirming types are convertible to base 60 numeric values.
|
||||
|
@ -585,7 +451,7 @@ private extension String {
|
|||
} else {
|
||||
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 = baseAndValue.1 + (digit * baseAndValue.0)
|
||||
let base = baseAndValue.0 * 60
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
// 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
|
||||
/// user info mapping. Similar to `Foundation.JSONDecoder`.
|
||||
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
|
||||
public var encoding: Parser.Encoding
|
||||
}
|
||||
|
@ -234,7 +213,7 @@ extension _Decoder: SingleValueDecodingContainer {
|
|||
|
||||
// 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 {return try construct(type) ?? type.init(from: self) }
|
||||
|
||||
|
@ -318,32 +297,6 @@ extension UInt16: ScalarConstructible {}
|
|||
// MARK: - ScalarConstructible UInt32 Conformance
|
||||
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
|
||||
|
||||
extension Decoder {
|
||||
|
@ -352,17 +305,3 @@ extension Decoder {
|
|||
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
|
||||
@_implementationOnly import CYaml
|
||||
#endif
|
||||
import Foundation
|
||||
|
||||
/// Produce a YAML string from objects.
|
||||
///
|
||||
|
@ -161,7 +160,7 @@ public func serialize<Nodes>(
|
|||
try emitter.open()
|
||||
try nodes.forEach(emitter.serialize)
|
||||
try emitter.close()
|
||||
return String(data: emitter.data, encoding: .utf8)!
|
||||
return emitter.data
|
||||
}
|
||||
|
||||
/// Produce a YAML string from a `Node`.
|
||||
|
@ -223,8 +222,8 @@ public final class Emitter {
|
|||
case crln
|
||||
}
|
||||
|
||||
/// Retrieve this Emitter's binary output.
|
||||
public internal(set) var data = Data()
|
||||
/// Retrieve this Emitter's string output.
|
||||
public internal(set) var data = ""
|
||||
|
||||
/// Configuration options to use when emitting YAML.
|
||||
public struct Options {
|
||||
|
@ -304,7 +303,7 @@ public final class Emitter {
|
|||
yaml_emitter_set_output(&self.emitter, { pointer, buffer, size in
|
||||
guard let buffer = buffer else { return 0 }
|
||||
let emitter = unsafeBitCast(pointer, to: Emitter.self)
|
||||
emitter.data.append(buffer, count: size)
|
||||
emitter.data.append(String(cString: buffer))
|
||||
return 1
|
||||
}, unsafeBitCast(self, to: UnsafeMutableRawPointer.self))
|
||||
|
||||
|
|
|
@ -26,14 +26,6 @@ extension Mark: CustomStringConvertible {
|
|||
extension Mark {
|
||||
/// Returns snippet string pointed by Mark instance from YAML String.
|
||||
public func snippet(from yaml: String) -> String {
|
||||
let contents = yaml.substring(at: line - 1)
|
||||
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) + "^"
|
||||
fatalError("Unimplemented")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
// Copyright (c) 2016 Yams. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// YAML Node.
|
||||
public enum Node: Hashable {
|
||||
/// Scalar node.
|
||||
|
@ -92,31 +90,11 @@ extension Node {
|
|||
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.
|
||||
public var int: Int? {
|
||||
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
|
||||
|
||||
/// 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
|
||||
@_implementationOnly import CYaml
|
||||
#endif
|
||||
import Foundation
|
||||
|
||||
/// Parse all YAML documents in a String
|
||||
/// and produce corresponding Swift objects.
|
||||
|
@ -127,25 +126,8 @@ public final class Parser {
|
|||
/// This value is case insensitive.
|
||||
public static var `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
|
||||
}()
|
||||
|
||||
/// The equivalent `Swift.Encoding` value for `self`.
|
||||
internal var swiftStringEncoding: String.Encoding {
|
||||
switch self {
|
||||
case .utf8:
|
||||
return .utf8
|
||||
case .utf16:
|
||||
return .utf16
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Encoding
|
||||
public let encoding: Encoding
|
||||
|
@ -182,40 +164,10 @@ public final class Parser {
|
|||
try utf8Slice.withUnsafeBytes(startParse(with:))
|
||||
}
|
||||
case .utf16:
|
||||
// use native endianness
|
||||
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:))
|
||||
fatalError("Unimplemented")
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
yaml_parser_delete(&parser)
|
||||
}
|
||||
|
@ -258,7 +210,6 @@ public final class Parser {
|
|||
private enum Buffer {
|
||||
case utf8View(String.UTF8View)
|
||||
case utf8Slice(ArraySlice<CChar>)
|
||||
case utf16(Data)
|
||||
}
|
||||
private var buffer: Buffer
|
||||
}
|
||||
|
@ -395,11 +346,7 @@ private class Event {
|
|||
return string(from: event.data.scalar.tag)
|
||||
}
|
||||
var scalarValue: String {
|
||||
// scalar may contain NULL characters
|
||||
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)!
|
||||
return String(cString: event.data.scalar.value)
|
||||
}
|
||||
|
||||
// sequence
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
// Copyright (c) 2017 Yams. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
#if os(iOS) || os(macOS) || os(watchOS) || os(tvOS)
|
||||
import Darwin
|
||||
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 {
|
||||
/// This value's `Node.scalar` representation.
|
||||
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 {
|
||||
/// This value's `Node.scalar` representation.
|
||||
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 {
|
||||
/// This value's `Node.scalar` representation.
|
||||
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 {
|
||||
let formatter = NumberFormatter()
|
||||
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 = YamsNumberFormatter()
|
||||
private let floatFormatter = YamsNumberFormatter()
|
||||
|
||||
private let doubleFormatter = numberFormatter(with: 15)
|
||||
private let floatFormatter = numberFormatter(with: 7)
|
||||
private struct YamsNumberFormatter {
|
||||
func string(for number: some FloatingPoint) -> String {
|
||||
"\(number)"
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Support `Float80`
|
||||
//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 {
|
||||
/// This value's `Node.scalar` representation.
|
||||
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
|
||||
|
||||
/// Types conforming to this protocol can be encoded by `YamlEncoder`.
|
||||
|
@ -275,8 +175,6 @@ extension YAMLEncodable where Self: ScalarRepresentable {
|
|||
}
|
||||
|
||||
extension Bool: YAMLEncodable {}
|
||||
extension Data: YAMLEncodable {}
|
||||
extension Decimal: YAMLEncodable {}
|
||||
extension Int: YAMLEncodable {}
|
||||
extension Int8: YAMLEncodable {}
|
||||
extension Int16: YAMLEncodable {}
|
||||
|
@ -287,41 +185,18 @@ extension UInt8: YAMLEncodable {}
|
|||
extension UInt16: YAMLEncodable {}
|
||||
extension UInt32: YAMLEncodable {}
|
||||
extension UInt64: YAMLEncodable {}
|
||||
extension URL: 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 {
|
||||
/// Returns this value wrapped in a `Node.scalar`.
|
||||
public func box() -> Node {
|
||||
return Node(formattedStringForCodable, Tag(.float))
|
||||
return Node("\(self)", Tag(.float))
|
||||
}
|
||||
}
|
||||
|
||||
extension Float: YAMLEncodable {
|
||||
/// Returns this value wrapped in a `Node.scalar`.
|
||||
public func box() -> Node {
|
||||
return Node(formattedStringForCodable, 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
|
||||
return Node("\(self)", Tag(.float))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,17 +6,14 @@
|
|||
// Copyright (c) 2016 Yams. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Class used to resolve nodes to tags based on customizable rules.
|
||||
public final class Resolver {
|
||||
/// Rule describing how to resolve tags from regex patterns.
|
||||
public struct Rule {
|
||||
/// The tag name this rule applies to.
|
||||
public let tag: Tag.Name
|
||||
fileprivate let regexp: NSRegularExpression
|
||||
/// The regex pattern used to resolve this rule.
|
||||
public var pattern: String { return regexp.pattern }
|
||||
private let _matches: (String) -> Bool
|
||||
func matches(in string: String) -> Bool { _matches(string) }
|
||||
|
||||
/// 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.
|
||||
public init(_ tag: Tag.Name, _ pattern: String) throws {
|
||||
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 {
|
||||
for rule in rules where rule.regexp.matches(in: string) {
|
||||
for rule in rules where rule.matches(in: string) {
|
||||
return rule.tag
|
||||
}
|
||||
return .str
|
||||
|
@ -153,21 +155,3 @@ extension Resolver.Rule {
|
|||
|
||||
// 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
|
||||
@_implementationOnly import CYaml
|
||||
#endif
|
||||
import Foundation
|
||||
|
||||
/// Errors thrown by Yams APIs.
|
||||
public enum YamlError: Error {
|
||||
|
@ -71,9 +70,7 @@ public enum YamlError: Error {
|
|||
case representer(problem: String)
|
||||
|
||||
/// String data could not be decoded with the specified encoding.
|
||||
///
|
||||
/// - parameter encoding: The string encoding used to decode the string data.
|
||||
case dataCouldNotBeDecoded(encoding: String.Encoding)
|
||||
case dataCouldNotBeDecoded
|
||||
|
||||
/// The error context.
|
||||
public struct Context: CustomStringConvertible {
|
||||
|
@ -158,13 +155,8 @@ extension YamlError: CustomStringConvertible {
|
|||
return "No error is produced"
|
||||
case .memory:
|
||||
return "Memory error"
|
||||
case let .reader(problem, offset, value, yaml):
|
||||
guard let (line, column, contents) = offset.flatMap(yaml.lineNumberColumnAndContents(at:)) else {
|
||||
case let .reader(problem, offset, 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):
|
||||
return "\(mark): error: scanner: \(context?.description ?? "")\(problem):\n" + mark.snippet(from: 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)
|
||||
case let .writer(problem), let .emitter(problem), let .representer(problem):
|
||||
return problem
|
||||
case .dataCouldNotBeDecoded(encoding: let encoding):
|
||||
return "String could not be decoded from data using '\(encoding)' encoding"
|
||||
case .dataCouldNotBeDecoded:
|
||||
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