From d8ed52cff5221a632eb0519ca9119637109c59dd Mon Sep 17 00:00:00 2001 From: JP Simard Date: Thu, 2 Nov 2017 00:05:22 -0700 Subject: [PATCH 01/35] Basic API doc generation --- .gitignore | 3 +++ .jazzy.yaml | 8 ++++++++ 2 files changed, 11 insertions(+) create mode 100644 .jazzy.yaml diff --git a/.gitignore b/.gitignore index 8d1ac22..339a553 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,6 @@ Carthage/Build fastlane/report.xml fastlane/screenshots + +# Docs +docs diff --git a/.jazzy.yaml b/.jazzy.yaml new file mode 100644 index 0000000..53ede41 --- /dev/null +++ b/.jazzy.yaml @@ -0,0 +1,8 @@ +module: Yams +author: JP Simard, Norio Nomura +author_url: http://jpsim.com +github_url: https://github.com/jpsim/Yams +github_file_prefix: https://github.com/jpsim/Yams/tree/docs +theme: fullwidth +clean: true +copyright: '© 2018 [JP Simard](http://jpsim.com) under MIT.' From 997e8de8840b56755927db0f746d5991f6d30dd2 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Thu, 2 Nov 2017 16:01:06 -0700 Subject: [PATCH 02/35] Add basic Docs.md --- Docs.md | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 Docs.md diff --git a/Docs.md b/Docs.md new file mode 100644 index 0000000..beb2ecc --- /dev/null +++ b/Docs.md @@ -0,0 +1,209 @@ +# Yams Documentation + +For installation instructions, see [README.md](README.md). + +API documentation coming soon. + +## Usage + +### Consume YAML + +Here's a simple example parsing a YAML array of strings: + +```swift +import Yams + +let yamlString = """ + - a + - b + - c + """ +do { + let yamlNode = try Yams.load(yaml: yamlString) + if let yamlArray = yamlNode as? [String] { + print(yamlArray) + } +} catch { + print("handle error: \(error)") +} + +// Prints: +// ["a", "b", "c"] +``` + +### Emit YAML + +Here's a simple example emitting YAML string from a Swift `Array`: + +```swift +import Yams + +do { + let yamlString = try Yams.serialize(node: ["a", "b", "c"]) + print(yamlString) +} catch { + print("handle error: \(error)") +} + +// Prints: +// - a +// - b +// - c +``` + +You can even customize the style: + +```swift +import Yams + +var node: Node = ["a", "b", "c"] +node.sequence?.style = .flow + +do { + let yamlString = try Yams.serialize(node: node) + print(yamlString) +} catch { + print("handle error: \(error)") +} + +// Prints: +// [a, b, c] +``` + +### Customize Parsing + +For example, say you only want the literals `true` and `false` to represent booleans, unlike the +YAML spec compliant boolean which also includes `on`/`off` and many others. + +You can customize Yams' Constructor map: + +```swift +import Yams + +extension Constructor { + public static func withBoolAsTrueFalse() -> Constructor { + var map = defaultMap + map[.bool] = Bool.constructUsingOnlyTrueAndFalse + return Constructor(map) + } +} + +private extension Bool { + static func constructUsingOnlyTrueAndFalse(from node: Node) -> Bool? { + assert(node.isScalar) + switch node.scalar!.string.lowercased() { + case "true": + return true + case "false": + return false + default: + return nil + } + } +} + +private extension Node { + var isScalar: Bool { + if case .scalar = self { + return true + } + return false + } +} + +// Usage: + +let yamlString = """ + - true + - on + - off + - false + """ +if let array = try? Yams.load(yaml: yamlString, .default, .withBoolAsTrueFalse()) as? [Any] { + print(array) +} + +// Prints: +// [true, "on", "off", false] +``` + +### Expanding Environment Variables + +For example: + +```swift +import Yams + +extension Constructor { + public static func withEnv(_ env: [String: String]) -> Constructor { + var map = defaultMap + map[.str] = String.constructExpandingEnvVars(env: env) + return Constructor(map) + } +} + +private extension String { + static func constructExpandingEnvVars(env: [String: String]) -> (_ node: Node) -> String? { + return { (node: Node) -> String? in + assert(node.isScalar) + return node.scalar!.string.expandingEnvVars(env: env) + } + } + + func expandingEnvVars(env: [String: String]) -> String { + var result = self + for (key, value) in env { + result = result.replacingOccurrences(of: "${\(key)}", with: value) + } + + return result + } +} + +// Usage: + +let yamlString = """ + - first + - ${SECOND} + - SECOND + """ +let env = ["SECOND": "2"] +if let array = try? Yams.load(yaml: yamlString, .default, .withEnv(env)) as? [String] { + print(array) +} + +// Prints: +// ["first", "2", "SECOND"] +``` + +### Converting Between Formats + +Because Yams conforms to Swift 4's Codable protocol and provides a YAML Encoder and Decoder, +you can easily convert between YAML and other formats that also provide Swift 4 Encoders and +Decoders, such as JSON and Plist. + +### Error Handling + +Failable operations in Yams throw Swift errors. + +### Types + +| Name | Yams Tag | YAML Tag | Swift Types | +|----------------|---------------|-------------------------------|--------------------------------| +| ... | `implicit` | `` | ... | +| ... | `nonSpecific` | `!` | ... | +| String | `str` | `tag:yaml.org,2002:str` | `String` | +| Sequence | `seq` | `tag:yaml.org,2002:seq` | `Array` | +| Map | `map` | `tag:yaml.org,2002:map` | `Dictionary` | +| Boolean | `bool` | `tag:yaml.org,2002:bool` | `Bool` | +| Floating Point | `float` | `tag:yaml.org,2002:float` | ... | +| Null | `null` | `tag:yaml.org,2002:null` | `Void` | +| Integer | `int` | `tag:yaml.org,2002:int` | `FixedWidthInteger` | +| ... | `binary` | `tag:yaml.org,2002:binary` | `Data` | +| ... | `merge` | `tag:yaml.org,2002:merge` | ... | +| ... | `omap` | `tag:yaml.org,2002:omap` | ... | +| ... | `pairs` | `tag:yaml.org,2002:pairs` | ... | +| Set | `set` | `tag:yaml.org,2002:set` | `Set` | +| Timestamp | `timestamp` | `tag:yaml.org,2002:timestamp` | `Date` | +| ... | `value` | `tag:yaml.org,2002:value` | ... | +| YAML | `yaml` | `tag:yaml.org,2002:yaml` | Unsupported | From 4f50d2101e1a6ad83b2755de011de409cf789eb0 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 15:02:45 -0700 Subject: [PATCH 03/35] Add docstrings to Constructor.swift --- Sources/Yams/Constructor.swift | 157 +++++++++++++++++++++++++++++++-- 1 file changed, 151 insertions(+), 6 deletions(-) diff --git a/Sources/Yams/Constructor.swift b/Sources/Yams/Constructor.swift index 8f8d472..512efa9 100644 --- a/Sources/Yams/Constructor.swift +++ b/Sources/Yams/Constructor.swift @@ -8,11 +8,20 @@ import Foundation +/// Constructors are used to translate `Node`s to Swift values. public final class Constructor { + /// Maps `Tag.Name`s to `Node.Scalar`s. public typealias ScalarMap = [Tag.Name: (Node.Scalar) -> Any?] + /// Maps `Tag.Name`s to `Node.Mapping`s. public typealias MappingMap = [Tag.Name: (Node.Mapping) -> Any?] + /// Maps `Tag.Name`s to `Node.Sequence`s. public typealias SequenceMap = [Tag.Name: (Node.Sequence) -> Any?] + /// Initialize a `Constructor` with the specified maps, falling back to default maps. + /// + /// - parameter scalarMap: Maps `Tag.Name`s to `Node.Scalar`s. + /// - parameter mappingMap: Maps `Tag.Name`s to `Node.Mapping`s. + /// - parameter sequenceMap: Maps `Tag.Name`s to `Node.Sequence`s. public init(_ scalarMap: ScalarMap = defaultScalarMap, _ mappingMap: MappingMap = defaultMappingMap, _ sequenceMap: SequenceMap = defaultSequenceMap) { @@ -21,6 +30,13 @@ public final class Constructor { self.sequenceMap = sequenceMap } + /// Constructs Swift values based on the maps this `Constructor` was initialized with. + /// + /// - parameter node: `Node` from which to extract an `Any` Swift value, if one was produced by the Node + /// type's relevant mapping on this `Constructor`. + /// + /// - returns: An `Any` Swift value, if one was produced by the Node type's relevant mapping on this + /// `Constructor`. public func any(from node: Node) -> Any { switch node { case .scalar(let scalar): @@ -46,10 +62,13 @@ public final class Constructor { private let sequenceMap: SequenceMap } +// MARK: - Default Mappings + extension Constructor { + /// The default `Constructor` to be used with APIs where none is explicitly provided. public static let `default` = Constructor() - // We can not write extension of map because that is alias of specialized dictionary + /// The default `Tag.Name` to `Node.Scalar` map. public static let defaultScalarMap: ScalarMap = [ // Failsafe Schema .str: String.construct, @@ -63,6 +82,7 @@ extension Constructor { .timestamp: Date.construct ] + /// The default `Tag.Name` to `Node.Mapping` map. public static let defaultMappingMap: MappingMap = [ .map: [AnyHashable: Any].construct_mapping, // http://yaml.org/type/index.html @@ -71,6 +91,7 @@ extension Constructor { // .value is supported in `String.construct` and `[AnyHashable: Any].construct_mapping`. ] + /// The default `Tag.Name` to `Node.Sequence` map. public static let defaultSequenceMap: SequenceMap = [ .seq: [Any].construct_seq, // http://yaml.org/type/index.html @@ -80,13 +101,28 @@ extension Constructor { } // MARK: - ScalarConstructible + +/// Types conforming to this protocol can be extracted `Node.Scalar`s. public protocol ScalarConstructible { - // We don't use overloading `init?(_ scalar: Node.Scalar)` - // because that causes difficulties on using `init` as closure + /// Construct an instance of `Self`, if possible, from the specified scalar. + /// + /// - parameter scalar: The `Node.Scalar` from which to extract a value of type `Self`, if possible. + /// + /// - returns: An instance of `Self`, if one was successfully extracted from the scalar. + /// + /// - note: We use static constructors to avoid overloading `init?(_ scalar: Node.Scalar)` which would + /// cause callsite ambiguities when using `init` as closure. static func construct(from scalar: Node.Scalar) -> Self? } +// MARK: - ScalarConstructible Bool Conformance + extension Bool: ScalarConstructible { + /// Construct an instance of `Bool`, if possible, from the specified scalar. + /// + /// - parameter scalar: The `Node.Scalar` from which to extract a value of type `Bool`, if possible. + /// + /// - returns: An instance of `Bool`, if one was successfully extracted from the scalar. public static func construct(from scalar: Node.Scalar) -> Bool? { switch scalar.string.lowercased() { case "true", "yes", "on": @@ -99,13 +135,27 @@ 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), @@ -168,10 +218,21 @@ extension Date: ScalarConstructible { ) } +// MARK: - ScalarConstructible Double Conformance extension Double: ScalarConstructible {} +// MARK: - ScalarConstructible Float Conformance extension Float: ScalarConstructible {} +// MARK: - ScalarConstructible FloatingPoint Conformance extension ScalarConstructible where Self: FloatingPoint & SexagesimalConvertible { + /// Construct an instance of `FloatingPoint & SexagesimalConvertible`, if possible, from the specified + /// scalar. + /// + /// - parameter scalar: The `Node.Scalar` from which to extract a value of type + /// `FloatingPoint & SexagesimalConvertible`, if possible. + /// + /// - returns: An instance of `FloatingPoint & SexagesimalConvertible`, if one was successfully extracted + /// from the scalar. public static func construct(from scalar: Node.Scalar) -> Self? { switch scalar.string { case ".inf", ".Inf", ".INF", "+.inf", "+.Inf", "+.INF": @@ -223,23 +284,49 @@ private extension FixedWidthInteger where Self: SexagesimalConvertible { } } +// MARK: - ScalarConstructible Int Conformance + extension Int: ScalarConstructible { + /// Construct an instance of `Int`, if possible, from the specified scalar. + /// + /// - parameter scalar: The `Node.Scalar` from which to extract a value of type `Int`, if possible. + /// + /// - returns: An instance of `Int`, if one was successfully extracted from the scalar. public static func construct(from scalar: Node.Scalar) -> Int? { return _construct(from: scalar) } } +// MARK: - ScalarConstructible UInt Conformance + extension UInt: ScalarConstructible { + /// Construct an instance of `UInt`, if possible, from the specified scalar. + /// + /// - parameter scalar: The `Node.Scalar` from which to extract a value of type `UInt`, if possible. + /// + /// - returns: An instance of `UInt`, if one was successfully extracted from the scalar. public static func construct(from scalar: Node.Scalar) -> UInt? { return _construct(from: scalar) } } +// MARK: - ScalarConstructible String Conformance + extension String: ScalarConstructible { + /// Construct an instance of `String`, if possible, from the specified scalar. + /// + /// - parameter scalar: The `Node.Scalar` from which to extract a value of type `String`, if possible. + /// + /// - returns: An instance of `String`, if one was successfully extracted from the scalar. public static func construct(from scalar: Node.Scalar) -> String? { return scalar.string } + /// Construct an instance of `String`, if possible, from the specified `Node`. + /// + /// - parameter node: The `Node` from which to extract a value of type `String`, if possible. + /// + /// - returns: An instance of `String`, if one was successfully extracted from the node. public static func construct(from node: Node) -> String? { // This will happen while `Dictionary.flatten_mapping()` if `node.tag.name` was `.value` if case let .mapping(mapping) = node { @@ -254,7 +341,13 @@ 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": @@ -265,8 +358,14 @@ extension NSNull/*: ScalarConstructible*/ { } } -// MARK: mapping +// MARK: Mapping + extension Dictionary { + /// Construct a `Dictionary`, if possible, from the specified mapping. + /// + /// - parameter mapping: The `Node.Mapping` from which to extract a `Dictionary`, if possible. + /// + /// - returns: An instance of `[AnyHashable: Any]`, if one was successfully extracted from the mapping. public static func construct_mapping(from mapping: Node.Mapping) -> [AnyHashable: Any]? { return _construct_mapping(from: mapping) } @@ -316,15 +415,22 @@ private extension Dictionary { } extension Set { + /// Construct a `Set`, if possible, from the specified mapping. + /// + /// - parameter mapping: The `Node.Mapping` from which to extract a `Set`, if possible. + /// + /// - returns: An instance of `Set`, if one was successfully extracted from the mapping. public static func construct_set(from mapping: Node.Mapping) -> Set? { // TODO: YAML supports Hashable elements other than str. return Set(mapping.map({ String.construct(from: $0.key)! as AnyHashable })) // Explicitly declaring the generic parameter as `` above is required, - // because this is inside extension of `Set` and Swift 3.0.2 can't infer the type without that. + // because this is inside extension of `Set` and Swift 3.0.2 to 4.1.0 can't infer the type without + // that. } } -// MARK: sequence +// MARK: Sequence + extension Array { public static func construct_seq(from sequence: Node.Sequence) -> [Any] { return sequence.map(sequence.tag.constructor.any) @@ -375,9 +481,30 @@ private extension StringProtocol { } // MARK: - SexagesimalConvertible + +/// Confirming types are convertible to base 60 numeric values. public protocol SexagesimalConvertible: ExpressibleByIntegerLiteral { + /// Creates a sexagesimal numeric value from the given string. + /// + /// - parameter string: The string from which to parse a sexagesimal value. + /// + /// - returns: A sexagesimal numeric value, if one was successfully parsed. static func create(from string: String) -> Self? + + /// Multiplies two sexagesimal numeric values. + /// + /// - parameter lhs: Left hand side multiplier. + /// - parameter rhs: Right hand side multiplier. + /// + /// - returns: The result of the multiplication. static func * (lhs: Self, rhs: Self) -> Self + + /// Adds two sexagesimal numeric values. + /// + /// - parameter lhs: Left hand side adder. + /// - parameter rhs: Right hand side adder. + /// + /// - returns: The result of the addition. static func + (lhs: Self, rhs: Self) -> Self } @@ -387,21 +514,39 @@ private extension SexagesimalConvertible { } } +// MARK: Default String to `LosslessStringConvertible` conversion. + extension SexagesimalConvertible where Self: LosslessStringConvertible { + /// Creates a sexagesimal numeric value from the given string. + /// + /// - parameter string: The string from which to parse a sexagesimal value. + /// + /// - returns: A sexagesimal numeric value, if one was successfully parsed. public static func create(from string: String) -> Self? { return Self(string) } } +// MARK: Default String to `FixedWidthInteger` conversion. + extension SexagesimalConvertible where Self: FixedWidthInteger { + /// Creates a sexagesimal numeric value from the given string. + /// + /// - parameter string: The string from which to parse a sexagesimal value. + /// + /// - returns: A sexagesimal numeric value, if one was successfully parsed. public static func create(from string: String) -> Self? { return Self(string, radix: 10) } } +// MARK: - SexagesimalConvertible Double Conformance extension Double: SexagesimalConvertible {} +// MARK: - SexagesimalConvertible Float Conformance extension Float: SexagesimalConvertible {} +// MARK: - SexagesimalConvertible Int Conformance extension Int: SexagesimalConvertible {} +// MARK: - SexagesimalConvertible Int Conformance extension UInt: SexagesimalConvertible {} private extension String { From 96ae475f29a5ab1345f02a15eb4fdfba2d064f29 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 15:14:12 -0700 Subject: [PATCH 04/35] Add docstrings to Decoder.swift --- Sources/Yams/Decoder.swift | 61 ++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/Sources/Yams/Decoder.swift b/Sources/Yams/Decoder.swift index 3ae54ee..19fba00 100644 --- a/Sources/Yams/Decoder.swift +++ b/Sources/Yams/Decoder.swift @@ -8,8 +8,21 @@ 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 { + /// Creates a `YAMLDecoder` instance. public init() {} + + /// Decode a `Decodable` type from a given `String` and optional user info mapping. + /// + /// - parameter type: `Decodable` type to decode. + /// - parameter yaml: YAML string 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` if something went wrong while decoding. public func decode(_ type: T.Type = T.self, from yaml: String, userInfo: [CodingUserInfoKey: Any] = [:]) throws -> T where T: Swift.Decodable { @@ -245,34 +258,70 @@ private func _typeMismatch(at codingPath: [CodingKey], expectation: Any.Type, re return .typeMismatch(expectation, context) } +// MARK: - ScalarConstructible FixedWidthInteger & SignedInteger Conformance + extension FixedWidthInteger where Self: SignedInteger { + /// Construct an instance of `Self`, if possible, from the specified scalar. + /// + /// - parameter scalar: The `Node.Scalar` from which to extract a value of type `Self`, if possible. + /// + /// - returns: An instance of `Self`, if one was successfully extracted from the scalar. public static func construct(from scalar: Node.Scalar) -> Self? { return Int.construct(from: scalar).flatMap(Self.init(exactly:)) } } +// MARK: - ScalarConstructible FixedWidthInteger & UnsignedInteger Conformance + extension FixedWidthInteger where Self: UnsignedInteger { + /// Construct an instance of `Self`, if possible, from the specified scalar. + /// + /// - parameter scalar: The `Node.Scalar` from which to extract a value of type `Self`, if possible. + /// + /// - returns: An instance of `Self`, if one was successfully extracted from the scalar. public static func construct(from scalar: Node.Scalar) -> Self? { return UInt.construct(from: scalar).flatMap(Self.init(exactly:)) } } -extension Int16: ScalarConstructible {} -extension Int32: ScalarConstructible {} -extension Int64: ScalarConstructible {} +// MARK: - ScalarConstructible Int8 Conformance extension Int8: ScalarConstructible {} -extension UInt16: ScalarConstructible {} -extension UInt32: ScalarConstructible {} -extension UInt64: ScalarConstructible {} +// MARK: - ScalarConstructible Int16 Conformance +extension Int16: ScalarConstructible {} +// MARK: - ScalarConstructible Int32 Conformance +extension Int32: ScalarConstructible {} +// MARK: - ScalarConstructible Int64 Conformance +extension Int64: ScalarConstructible {} +// MARK: - ScalarConstructible UInt8 Conformance extension UInt8: ScalarConstructible {} +// MARK: - ScalarConstructible UInt16 Conformance +extension UInt16: ScalarConstructible {} +// MARK: - ScalarConstructible UInt32 Conformance +extension UInt32: ScalarConstructible {} +// MARK: - ScalarConstructible UInt64 Conformance +extension UInt64: 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) } From f851400e156ac44fdf81f2d3ae31d1617c6b841b Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 15:42:59 -0700 Subject: [PATCH 05/35] Add dosctrings to Emitter.swift --- Sources/Yams/Emitter.swift | 200 ++++++++++++++++++++----------------- 1 file changed, 106 insertions(+), 94 deletions(-) diff --git a/Sources/Yams/Emitter.swift b/Sources/Yams/Emitter.swift index 6e7817e..69e54b5 100644 --- a/Sources/Yams/Emitter.swift +++ b/Sources/Yams/Emitter.swift @@ -7,24 +7,25 @@ // #if SWIFT_PACKAGE - import CYaml +import CYaml #endif import Foundation -/// Produce YAML String from objects +/// Produce a YAML string from objects. /// -/// - Parameters: -/// - objects: Sequence of Object -/// - canonical: output should be the "canonical" format as in the YAML specification. -/// - indent: the intendation increment. -/// - width: the preferred line width. @c -1 means unlimited. -/// - allowUnicode: unescaped non-ASCII characters are allowed if true. -/// - lineBreak: preferred line break. -/// - explicitStart: explicit document start `---` -/// - explicitEnd: explicit document end `...` -/// - version: YAML version directive -/// - Returns: YAML String -/// - Throws: `YamlError` +/// - parameter objects: Sequence of Objects. +/// - parameter canonical: Output should be the "canonical" format as in the YAML specification. +/// - parameter indent: The intendation increment. +/// - parameter width: The preferred line width. @c -1 means unlimited. +/// - parameter allowUnicode: Unescaped non-ASCII characters are allowed if true. +/// - parameter lineBreak: Preferred line break. +/// - parameter explicitStart: Explicit document start `---`. +/// - parameter explicitEnd: Explicit document end `...`. +/// - parameter version: YAML version directive. +/// +/// - returns: YAML string. +/// +/// - throws: `YamlError`. public func dump( objects: Objects, canonical: Bool = false, @@ -36,41 +37,44 @@ public func dump( explicitEnd: Bool = false, version: (major: Int, minor: Int)? = nil, sortKeys: Bool = false) throws -> String - where Objects: Sequence { - func representable(from object: Any) throws -> NodeRepresentable { - if let representable = object as? NodeRepresentable { - return representable - } - throw YamlError.emitter(problem: "\(object) does not conform to NodeRepresentable!") + where Objects: Sequence +{ + func representable(from object: Any) throws -> NodeRepresentable { + if let representable = object as? NodeRepresentable { + return representable } - let nodes = try objects.map(representable(from:)).map { try $0.represented() } - return try serialize( - nodes: nodes, - canonical: canonical, - indent: indent, - width: width, - allowUnicode: allowUnicode, - lineBreak: lineBreak, - explicitStart: explicitStart, - explicitEnd: explicitEnd, - version: version, - sortKeys: sortKeys) + throw YamlError.emitter(problem: "\(object) does not conform to NodeRepresentable!") + } + let nodes = try objects.map(representable(from:)).map { try $0.represented() } + return try serialize( + nodes: nodes, + canonical: canonical, + indent: indent, + width: width, + allowUnicode: allowUnicode, + lineBreak: lineBreak, + explicitStart: explicitStart, + explicitEnd: explicitEnd, + version: version, + sortKeys: sortKeys + ) } -/// Produce YAML String from object +/// Produce a YAML string from an object. /// -/// - Parameters: -/// - object: Object -/// - canonical: output should be the "canonical" format as in the YAML specification. -/// - indent: the intendation increment. -/// - width: the preferred line width. @c -1 means unlimited. -/// - allowUnicode: unescaped non-ASCII characters are allowed if true. -/// - lineBreak: preferred line break. -/// - explicitStart: explicit document start `---` -/// - explicitEnd: explicit document end `...` -/// - version: YAML version directive -/// - Returns: YAML String -/// - Throws: `YamlError` +/// - parameter object: Object. +/// - parameter canonical: Output should be the "canonical" format as in the YAML specification. +/// - parameter indent: The intendation increment. +/// - parameter width: The preferred line width. @c -1 means unlimited. +/// - parameter allowUnicode: Unescaped non-ASCII characters are allowed if true. +/// - parameter lineBreak: Preferred line break. +/// - parameter explicitStart: Explicit document start `---`. +/// - parameter explicitEnd: Explicit document end `...`. +/// - parameter version: YAML version directive. +/// +/// - returns: YAML string. +/// +/// - throws: `YamlError`. public func dump( object: Any?, canonical: Bool = false, @@ -81,7 +85,8 @@ public func dump( explicitStart: Bool = false, explicitEnd: Bool = false, version: (major: Int, minor: Int)? = nil, - sortKeys: Bool = false) throws -> String { + sortKeys: Bool = false) throws -> String +{ return try serialize( node: object.represented(), canonical: canonical, @@ -92,23 +97,25 @@ public func dump( explicitStart: explicitStart, explicitEnd: explicitEnd, version: version, - sortKeys: sortKeys) + sortKeys: sortKeys + ) } -/// Produce YAML String from `Node` +/// Produce a YAML string from a `Node`. /// -/// - Parameters: -/// - nodes: Sequence of `Node` -/// - canonical: output should be the "canonical" format as in the YAML specification. -/// - indent: the intendation increment. -/// - width: the preferred line width. @c -1 means unlimited. -/// - allowUnicode: unescaped non-ASCII characters are allowed if true. -/// - lineBreak: preferred line break. -/// - explicitStart: explicit document start `---` -/// - explicitEnd: explicit document end `...` -/// - version: YAML version directive -/// - Returns: YAML String -/// - Throws: `YamlError` +/// - parameter nodes: Sequence of `Node`s. +/// - parameter canonical: Output should be the "canonical" format as in the YAML specification. +/// - parameter indent: The intendation increment. +/// - parameter width: The preferred line width. @c -1 means unlimited. +/// - parameter allowUnicode: Unescaped non-ASCII characters are allowed if true. +/// - parameter lineBreak: Preferred line break. +/// - parameter explicitStart: Explicit document start `---`. +/// - parameter explicitEnd: Explicit document end `...`. +/// - parameter version: YAML version directive. +/// +/// - returns: YAML string. +/// +/// - throws: `YamlError`. public func serialize( nodes: Nodes, canonical: Bool = false, @@ -120,41 +127,44 @@ public func serialize( explicitEnd: Bool = false, version: (major: Int, minor: Int)? = nil, sortKeys: Bool = false) throws -> String - where Nodes: Sequence, Nodes.Iterator.Element == Node { - let emitter = Emitter( - canonical: canonical, - indent: indent, - width: width, - allowUnicode: allowUnicode, - lineBreak: lineBreak, - explicitStart: explicitStart, - explicitEnd: explicitEnd, - version: version, - sortKeys: sortKeys) - try emitter.open() - try nodes.forEach(emitter.serialize) - try emitter.close() - #if USE_UTF8 - return String(data: emitter.data, encoding: .utf8)! - #else - return String(data: emitter.data, encoding: .utf16)! - #endif + where Nodes: Sequence, Nodes.Iterator.Element == Node +{ + let emitter = Emitter( + canonical: canonical, + indent: indent, + width: width, + allowUnicode: allowUnicode, + lineBreak: lineBreak, + explicitStart: explicitStart, + explicitEnd: explicitEnd, + version: version, + sortKeys: sortKeys + ) + try emitter.open() + try nodes.forEach(emitter.serialize) + try emitter.close() +#if USE_UTF8 + return String(data: emitter.data, encoding: .utf8)! +#else + return String(data: emitter.data, encoding: .utf16)! +#endif } -/// Produce YAML String from `Node` +/// Produce a YAML string from a `Node`. /// -/// - Parameters: -/// - node: `Node` -/// - canonical: output should be the "canonical" format as in the YAML specification. -/// - indent: the intendation increment. -/// - width: the preferred line width. @c -1 means unlimited. -/// - allowUnicode: unescaped non-ASCII characters are allowed if true. -/// - lineBreak: preferred line break. -/// - explicitStart: explicit document start `---` -/// - explicitEnd: explicit document end `...` -/// - version: YAML version directive -/// - Returns: YAML String -/// - Throws: `YamlError` +/// - parameter node: `Node`. +/// - parameter canonical: Output should be the "canonical" format as in the YAML specification. +/// - parameter indent: The intendation increment. +/// - parameter width: The preferred line width. @c -1 means unlimited. +/// - parameter allowUnicode: Unescaped non-ASCII characters are allowed if true. +/// - parameter lineBreak: Preferred line break. +/// - parameter explicitStart: Explicit document start `---`. +/// - parameter explicitEnd: Explicit document end `...`. +/// - parameter version: YAML version directive. +/// +/// - returns: YAML string. +/// +/// - throws: `YamlError`. public func serialize( node: Node, canonical: Bool = false, @@ -165,7 +175,8 @@ public func serialize( explicitStart: Bool = false, explicitEnd: Bool = false, version: (major: Int, minor: Int)? = nil, - sortKeys: Bool = false) throws -> String { + sortKeys: Bool = false) throws -> String +{ return try serialize( nodes: [node], canonical: canonical, @@ -176,7 +187,8 @@ public func serialize( explicitStart: explicitStart, explicitEnd: explicitEnd, version: version, - sortKeys: sortKeys) + sortKeys: sortKeys + ) } public final class Emitter { From 454544bd15a1a7b31e65777ac229e59f0cc64618 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 15:58:31 -0700 Subject: [PATCH 06/35] Add docstrings to Emitter.swift --- .swiftlint.yml | 4 ++ Sources/Yams/Emitter.swift | 96 ++++++++++++++++++++++++++------------ 2 files changed, 71 insertions(+), 29 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 3922959..12eaa85 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,3 +1,7 @@ disabled_rules: - identifier_name line_length: 120 +identifier_name: + excluded: + - cr + - ln diff --git a/Sources/Yams/Emitter.swift b/Sources/Yams/Emitter.swift index 69e54b5..d1ef49b 100644 --- a/Sources/Yams/Emitter.swift +++ b/Sources/Yams/Emitter.swift @@ -37,8 +37,7 @@ public func dump( explicitEnd: Bool = false, version: (major: Int, minor: Int)? = nil, sortKeys: Bool = false) throws -> String - where Objects: Sequence -{ + where Objects: Sequence { func representable(from object: Any) throws -> NodeRepresentable { if let representable = object as? NodeRepresentable { return representable @@ -85,8 +84,7 @@ public func dump( explicitStart: Bool = false, explicitEnd: Bool = false, version: (major: Int, minor: Int)? = nil, - sortKeys: Bool = false) throws -> String -{ + sortKeys: Bool = false) throws -> String { return try serialize( node: object.represented(), canonical: canonical, @@ -127,8 +125,7 @@ public func serialize( explicitEnd: Bool = false, version: (major: Int, minor: Int)? = nil, sortKeys: Bool = false) throws -> String - where Nodes: Sequence, Nodes.Iterator.Element == Node -{ + where Nodes: Sequence, Nodes.Iterator.Element == Node { let emitter = Emitter( canonical: canonical, indent: indent, @@ -175,8 +172,7 @@ public func serialize( explicitStart: Bool = false, explicitEnd: Bool = false, version: (major: Int, minor: Int)? = nil, - sortKeys: Bool = false) throws -> String -{ + sortKeys: Bool = false) throws -> String { return try serialize( nodes: [node], canonical: canonical, @@ -191,22 +187,25 @@ public func serialize( ) } +/// Class responsible for emitting libYAML events. public final class Emitter { + /// Line break options to use when emitting YAML. public enum LineBreak { /// Use CR for line breaks (Mac style). - case cr // swiftlint:disable:this identifier_name + case cr /// Use LN for line breaks (Unix style). - case ln // swiftlint:disable:this identifier_name + case ln /// Use CR LN for line breaks (DOS style). case crln } - public var data = Data() + internal var data = Data() + /// Configuration options to use when emitting YAML. public struct Options { - /// Set if the output should be in the "canonical" format as in the YAML specification. + /// Set if the output should be in the "canonical" format described in the YAML specification. public var canonical: Bool = false - /// Set the intendation increment. + /// Set the indentation value. public var indent: Int = 0 /// Set the preferred line width. -1 means unlimited. public var width: Int = 0 @@ -219,19 +218,32 @@ public final class Emitter { var explicitStart: Bool = false var explicitEnd: Bool = false - /// The %YAML directive value or nil + /// The `%YAML` directive value or nil. public var version: (major: Int, minor: Int)? /// Set if emitter should sort keys in lexicographic order. public var sortKeys: Bool = false } + /// Configuration options to use when emitting YAML. public var options: Options { didSet { applyOptionsToEmitter() } } + /// Create an `Emitter` with the specified options. + /// + /// - parameter canonical: Set if the output should be in the "canonical" format described in the YAML + /// specification. + /// - parameter indent: Set the indentation value. + /// - parameter width: Set the preferred line width. -1 means unlimited. + /// - parameter allowUnicode: Set if unescaped non-ASCII characters are allowed. + /// - parameter lineBreak: Set the preferred line break. + /// - parameter explicitStart: Explicit document start `---`. + /// - parameter explicitEnd: Explicit document end `...`. + /// - parameter version: The `%YAML` directive value or nil. + /// - parameter sortKeys: Set if emitter should sort keys in lexicographic order. public init(canonical: Bool = false, indent: Int = 0, width: Int = 0, @@ -261,27 +273,30 @@ public final class Emitter { applyOptionsToEmitter() - #if USE_UTF8 - yaml_emitter_set_encoding(&emitter, YAML_UTF8_ENCODING) - #else - yaml_emitter_set_encoding(&emitter, isLittleEndian ? YAML_UTF16LE_ENCODING : YAML_UTF16BE_ENCODING) - #endif +#if USE_UTF8 + yaml_emitter_set_encoding(&emitter, YAML_UTF8_ENCODING) +#else + yaml_emitter_set_encoding(&emitter, isLittleEndian ? YAML_UTF16LE_ENCODING : YAML_UTF16BE_ENCODING) +#endif } deinit { yaml_emitter_delete(&emitter) } + /// Open & initialize the emmitter. + /// + /// - throws: `YamlError` if the `Emitter` was already opened or closed. public func open() throws { switch state { case .initialized: var event = yaml_event_t() - #if USE_UTF8 - yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING) - #else - let encoding = isLittleEndian ? YAML_UTF16LE_ENCODING : YAML_UTF16BE_ENCODING - yaml_stream_start_event_initialize(&event, encoding) - #endif +#if USE_UTF8 + yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING) +#else + let encoding = isLittleEndian ? YAML_UTF16LE_ENCODING : YAML_UTF16BE_ENCODING + yaml_stream_start_event_initialize(&event, encoding) +#endif try emit(&event) state = .opened case .opened: @@ -291,6 +306,9 @@ public final class Emitter { } } + /// Close the `Emitter.` + /// + /// - throws: `YamlError` if the `Emitter` hasn't yet been initialized. public func close() throws { switch state { case .initialized: @@ -305,6 +323,11 @@ public final class Emitter { } } + /// Ingest a `Node` to include when emitting the YAML output. + /// + /// - parameter node: The `Node` to serialize. + /// + /// - throws: `YamlError` if the `Emitter` hasn't yet been opened or has been closed. public func serialize(node: Node) throws { switch state { case .initialized: @@ -330,7 +353,7 @@ public final class Emitter { try emit(&event) } - // private + // MARK: Private private var emitter = yaml_emitter_t() private enum State { case initialized, opened, closed } @@ -349,10 +372,24 @@ public final class Emitter { } } +// MARK: - Options Initializer + extension Emitter.Options { - // initializer without exposing internal properties + /// Create `Emitter.Options` with the specified values. + /// + /// - parameter canonical: Set if the output should be in the "canonical" format described in the YAML + /// specification. + /// - parameter indent: Set the indentation value. + /// - parameter width: Set the preferred line width. -1 means unlimited. + /// - parameter allowUnicode: Set if unescaped non-ASCII characters are allowed. + /// - parameter lineBreak: Set the preferred line break. + /// - parameter explicitStart: Explicit document start `---`. + /// - parameter explicitEnd: Explicit document end `...`. + /// - parameter version: The `%YAML` directive value or nil. + /// - parameter sortKeys: Set if emitter should sort keys in lexicographic order. public init(canonical: Bool = false, indent: Int = 0, width: Int = 0, allowUnicode: Bool = false, - lineBreak: Emitter.LineBreak = .ln, version: (major: Int, minor: Int)? = nil, sortKeys: Bool = false) { + lineBreak: Emitter.LineBreak = .ln, version: (major: Int, minor: Int)? = nil, + sortKeys: Bool = false) { self.canonical = canonical self.indent = indent self.width = width @@ -363,7 +400,8 @@ extension Emitter.Options { } } -// MARK: implementation details +// MARK: Implementation Details + extension Emitter { private func emit(_ event: UnsafeMutablePointer) throws { guard yaml_emitter_emit(&emitter, event) == 1 else { From eb4bb2a945953550e6f82ff721c6bc9461b1f4ad Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 16:01:01 -0700 Subject: [PATCH 07/35] Adjust indentation in shim.swift --- Sources/Yams/shim.swift | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/Sources/Yams/shim.swift b/Sources/Yams/shim.swift index 0b2c120..e34748f 100644 --- a/Sources/Yams/shim.swift +++ b/Sources/Yams/shim.swift @@ -7,23 +7,19 @@ // #if !swift(>=4.1) - - extension Sequence { - func compactMap( - _ transform: (Self.Element - ) throws -> ElementOfResult?) rethrows -> [ElementOfResult] { - return try flatMap(transform) - } +extension Sequence { + func compactMap( + _ transform: (Self.Element + ) throws -> ElementOfResult?) rethrows -> [ElementOfResult] { + return try flatMap(transform) } - +} #endif #if os(Linux) && !swift(>=4.2) - - extension Substring { - func hasPrefix(_ prefix: String) -> Bool { - return String(self).hasPrefix(prefix) - } +extension Substring { + func hasPrefix(_ prefix: String) -> Bool { + return String(self).hasPrefix(prefix) } - +} #endif From 5f1f10fb5273760bbe2eedc98595b323810ec483 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 16:01:27 -0700 Subject: [PATCH 08/35] Adjust indentation in Tag.swift --- Sources/Yams/Tag.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Yams/Tag.swift b/Sources/Yams/Tag.swift index 48fc676..75aaf1f 100644 --- a/Sources/Yams/Tag.swift +++ b/Sources/Yams/Tag.swift @@ -7,7 +7,7 @@ // #if SWIFT_PACKAGE - import CYaml +import CYaml #endif import Foundation From d9416f03f88865e32e616369ba04bbc8c246f2c0 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 16:02:27 -0700 Subject: [PATCH 09/35] Adjust indentation in Parser.swift --- Sources/Yams/Parser.swift | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Sources/Yams/Parser.swift b/Sources/Yams/Parser.swift index a214d07..a07ae15 100644 --- a/Sources/Yams/Parser.swift +++ b/Sources/Yams/Parser.swift @@ -7,7 +7,7 @@ // #if SWIFT_PACKAGE - import CYaml +import CYaml #endif import Foundation @@ -113,23 +113,23 @@ public final class Parser { self.constructor = constructor yaml_parser_initialize(&parser) - #if USE_UTF8 - yaml_parser_set_encoding(&parser, YAML_UTF8_ENCODING) - utf8CString = string.utf8CString - utf8CString.withUnsafeBytes { bytes in - let input = bytes.baseAddress?.assumingMemoryBound(to: UInt8.self) - yaml_parser_set_input_string(&parser, input, bytes.count - 1) - } - #else - // use native endian - let isLittleEndian = 1 == 1.littleEndian - yaml_parser_set_encoding(&parser, isLittleEndian ? YAML_UTF16LE_ENCODING : YAML_UTF16BE_ENCODING) - let encoding: String.Encoding = isLittleEndian ? .utf16LittleEndian : .utf16BigEndian - data = yaml.data(using: encoding)! - data.withUnsafeBytes { bytes in - yaml_parser_set_input_string(&parser, bytes, data.count) - } - #endif +#if USE_UTF8 + yaml_parser_set_encoding(&parser, YAML_UTF8_ENCODING) + utf8CString = string.utf8CString + utf8CString.withUnsafeBytes { bytes in + let input = bytes.baseAddress?.assumingMemoryBound(to: UInt8.self) + yaml_parser_set_input_string(&parser, input, bytes.count - 1) + } +#else + // use native endian + let isLittleEndian = 1 == 1.littleEndian + yaml_parser_set_encoding(&parser, isLittleEndian ? YAML_UTF16LE_ENCODING : YAML_UTF16BE_ENCODING) + let encoding: String.Encoding = isLittleEndian ? .utf16LittleEndian : .utf16BigEndian + data = yaml.data(using: encoding)! + data.withUnsafeBytes { bytes in + yaml_parser_set_input_string(&parser, bytes, data.count) + } +#endif try parse() // Drop YAML_STREAM_START_EVENT } From de565215f42110fd481a34425e5ccb5aa7c49b18 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 16:02:54 -0700 Subject: [PATCH 10/35] Adjust indentation in Representer.swift --- Sources/Yams/Representer.swift | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Sources/Yams/Representer.swift b/Sources/Yams/Representer.swift index e713c7d..0976f6b 100644 --- a/Sources/Yams/Representer.swift +++ b/Sources/Yams/Representer.swift @@ -7,7 +7,7 @@ // #if SWIFT_PACKAGE - import CYaml +import CYaml #endif import Foundation @@ -87,18 +87,18 @@ extension Date: ScalarRepresentable { private var iso8601String: String { let calendar = Calendar(identifier: .gregorian) let nanosecond = calendar.component(.nanosecond, from: self) - #if os(Linux) - // swift-corelibs-foundation has bug with nanosecond. - // https://bugs.swift.org/browse/SR-3158 +#if os(Linux) + // swift-corelibs-foundation has bug with nanosecond. + // https://bugs.swift.org/browse/SR-3158 + return iso8601Formatter.string(from: self) +#else + if nanosecond != 0 { + return iso8601WithFractionalSecondFormatter.string(from: self) + .trimmingCharacters(in: characterSetZero) + "Z" + } else { return iso8601Formatter.string(from: self) - #else - if nanosecond != 0 { - return iso8601WithFractionalSecondFormatter.string(from: self) - .trimmingCharacters(in: characterSetZero) + "Z" - } else { - return iso8601Formatter.string(from: self) - } - #endif + } +#endif } private var iso8601StringWithFullNanosecond: String { From 50420f0a87a52b5ee03fb54c8b65c8d6d0b584cf Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 16:03:38 -0700 Subject: [PATCH 11/35] Adjust indentation in YamlError.swift --- Sources/Yams/YamlError.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/Yams/YamlError.swift b/Sources/Yams/YamlError.swift index c3bbd3b..54ec235 100644 --- a/Sources/Yams/YamlError.swift +++ b/Sources/Yams/YamlError.swift @@ -158,13 +158,13 @@ extension YamlError: CustomStringConvertible { extension YamlError { private func markAndSnippet(from yaml: String, _ byteOffset: Int) -> (Mark, String)? { - #if USE_UTF8 - guard let (line, column, contents) = yaml.utf8LineNumberColumnAndContents(at: byteOffset) - else { return nil } - #else - guard let (line, column, contents) = yaml.utf16LineNumberColumnAndContents(at: byteOffset / 2) - else { return nil } - #endif +#if USE_UTF8 + guard let (line, column, contents) = yaml.utf8LineNumberColumnAndContents(at: byteOffset) + else { return nil } +#else + guard let (line, column, contents) = yaml.utf16LineNumberColumnAndContents(at: byteOffset / 2) + else { return nil } +#endif return (Mark(line: line + 1, column: column + 1), contents) } } From c971f1fb68170c77131df318b9a0c2d8f4e010e2 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 16:12:59 -0700 Subject: [PATCH 12/35] Add docstrings to Encoder.swift --- Sources/Yams/Encoder.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Sources/Yams/Encoder.swift b/Sources/Yams/Encoder.swift index 1dba056..c7cbda1 100644 --- a/Sources/Yams/Encoder.swift +++ b/Sources/Yams/Encoder.swift @@ -8,10 +8,26 @@ import Foundation +/// `Codable`-style `Encoder` that can be used to encode an `Encodable` type to a YAML string using optional +/// user info mapping. Similar to `Foundation.JSONEncoder`. public class YAMLEncoder { + /// Options to use when encoding to YAML. public typealias Options = Emitter.Options + + /// Options to use when encoding to YAML. public var options = Options() + + /// Creates a `YAMLEncoder` instance. public init() {} + + /// Encode a value of type `T` to a YAML string. + /// + /// - value: Value to encode. + /// - userInfo: Additional key/values which can be used when looking up keys to encode. + /// + /// - returns: The YAML string. + /// + /// - throws: `EncodingError` if something went wrong while encoding. public func encode(_ value: T, userInfo: [CodingUserInfoKey: Any] = [:]) throws -> String { do { let encoder = _Encoder(userInfo: userInfo) From cdea3d3d4b239e0f47bacbf825a3a41502550e52 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 16:16:07 -0700 Subject: [PATCH 13/35] Tweak docstrings in Mark.swift --- Sources/Yams/Mark.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Sources/Yams/Mark.swift b/Sources/Yams/Mark.swift index c262d05..51798db 100644 --- a/Sources/Yams/Mark.swift +++ b/Sources/Yams/Mark.swift @@ -8,21 +8,25 @@ import Foundation -/// The pointer position +/// The pointer position. public struct Mark { - /// line start from 1 + /// Line number starting from 1. public let line: Int - /// column start from 1. libYAML counts columns in `UnicodeScalar`. + /// Column number starting from 1. libYAML counts columns in `UnicodeScalar`. public let column: Int } +// MARK: - CustomStringConvertible Conformance + extension Mark: CustomStringConvertible { /// A textual representation of this instance. public var description: String { return "\(line):\(column)" } } +// MARK: Snippet + 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 { let contents = yaml.substring(at: line - 1) let columnIndex = contents.unicodeScalars From 05fc492d44f9e8c9e305504a9d0d5fc336a6afe9 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 16:37:33 -0700 Subject: [PATCH 14/35] Add docstrings to Node.swift --- Sources/Yams/Node.swift | 74 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/Sources/Yams/Node.swift b/Sources/Yams/Node.swift index 9d42080..2d54f9a 100644 --- a/Sources/Yams/Node.swift +++ b/Sources/Yams/Node.swift @@ -8,28 +8,51 @@ import Foundation +/// YAML Node. public enum Node { + /// Scalar node. case scalar(Scalar) + /// Mapping node. case mapping(Mapping) + /// Sequence node. case sequence(Sequence) } extension Node { + /// Create a `Node.scalar` with a string, tag & scalar style. + /// + /// - parameter string: String value for this node. + /// - parameter tag: Tag for this node. + /// - parameter style: Style to use when emitting this node. public init(_ string: String, _ tag: Tag = .implicit, _ style: Scalar.Style = .any) { self = .scalar(.init(string, tag, style)) } + /// Create a `Node.mapping` with a sequence of node pairs, tag & scalar style. + /// + /// - parameter pairs: Pairs of nodes to use for this node. + /// - parameter tag: Tag for this node. + /// - parameter style: Style to use when emitting this node. public init(_ pairs: [(Node, Node)], _ tag: Tag = .implicit, _ style: Mapping.Style = .any) { self = .mapping(.init(pairs, tag, style)) } + /// Create a `Node.sequence` with a sequence of nodes, tag & scalar style. + /// + /// - parameter nodes: Sequence of nodes to use for this node. + /// - parameter tag: Tag for this node. + /// - parameter style: Style to use when emitting this node. public init(_ nodes: [Node], _ tag: Tag = .implicit, _ style: Sequence.Style = .any) { self = .sequence(.init(nodes, tag, style)) } } +// MARK: - Public Node Members + extension Node { - /// Accessing this property causes the tag to be resolved by tag.resolver. + /// The tag for this node. + /// + /// - note: Accessing this property causes the tag to be resolved by tag.resolver. public var tag: Tag { switch self { case let .scalar(scalar): return scalar.resolvedTag @@ -38,6 +61,7 @@ extension Node { } } + /// The location for this node. public var mark: Mark? { switch self { case let .scalar(scalar): return scalar.mark @@ -46,54 +70,67 @@ extension Node { } } - // MARK: typed accessor properties + // MARK: - Typed accessor properties + + /// This node as an `Any`, if convertible. public var any: Any { return tag.constructor.any(from: self) } + /// This node as a `String`, if convertible. public var string: String? { return String.construct(from: self) } + /// This node as a `Bool`, if convertible. public var bool: Bool? { return scalar.flatMap(Bool.construct) } + /// This node as a `Double`, if convertible. public var float: Double? { 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) } // MARK: Typed accessor methods - /// - Returns: Array of `Node` + /// Returns this node mapped as an `Array`. If the node isn't a `Node.sequence`, the array will be + /// empty. public func array() -> [Node] { return sequence.map(Array.init) ?? [] } /// Typed Array using type parameter: e.g. `array(of: String.self)` /// - /// - Parameter type: Type conforms to ScalarConstructible - /// - Returns: Array of `Type` + /// - parameter type: Type conforming to `ScalarConstructible`. + /// + /// - returns: Array of `Type`. public func array(of type: Type.Type = Type.self) -> [Type] { return sequence?.compactMap { $0.scalar.flatMap(type.construct) } ?? [] } + /// If the node is a `.sequence` or `.mapping`, set or get the specified node. + /// If the node is a `.scalar`, this is a no-op. public subscript(node: Node) -> Node? { get { switch self { @@ -120,6 +157,9 @@ extension Node { } } + /// If the node is a `.sequence` or `.mapping`, set or get the specified parameter's `Node` + /// representation. + /// If the node is a `.scalar`, this is a no-op. public subscript(representable: NodeRepresentable) -> Node? { get { guard let node = try? representable.represented() else { return nil } @@ -131,6 +171,8 @@ extension Node { } } + /// If the node is a `.sequence` or `.mapping`, set or get the specified string's `Node` representation. + /// If the node is a `.scalar`, this is a no-op. public subscript(string: String) -> Node? { get { return self[Node(string, tag.copy(with: .implicit))] @@ -142,7 +184,9 @@ extension Node { } // MARK: Hashable + extension Node: Hashable { + /// This `Node`'s Hashable `hashValue`. public var hashValue: Int { switch self { case let .scalar(scalar): @@ -154,6 +198,12 @@ extension Node: Hashable { } } + /// Returns true if both nodes are equal. + /// + /// - parameter lhs: The left hand side Node to compare. + /// - parameter rhs: The right hand side Node to compare. + /// + /// - returns: True if both nodes are equal. public static func == (lhs: Node, rhs: Node) -> Bool { switch (lhs, rhs) { case let (.scalar(lhs), .scalar(rhs)): @@ -168,7 +218,15 @@ extension Node: Hashable { } } +// MARK: Comparable + extension Node: Comparable { + /// Returns true if `lhs` is ordered before `rhs`. + /// + /// - parameter lhs: The left hand side Node to compare. + /// - parameter rhs: The right hand side Node to compare. + /// + /// - returns: True if `lhs` is ordered before `rhs`. public static func < (lhs: Node, rhs: Node) -> Bool { switch (lhs, rhs) { case let (.scalar(lhs), .scalar(rhs)): @@ -197,31 +255,37 @@ extension Array where Element: Comparable { } // MARK: - ExpressibleBy*Literal + extension Node: ExpressibleByArrayLiteral { + /// Create a `Node.sequence` from an array literal of `Node`s. public init(arrayLiteral elements: Node...) { self = .sequence(.init(elements)) } } extension Node: ExpressibleByDictionaryLiteral { + /// Create a `Node.mapping` from a dictionary literal of `Node`s. public init(dictionaryLiteral elements: (Node, Node)...) { self = Node(elements) } } extension Node: ExpressibleByFloatLiteral { + /// Create a `Node.scalar` from a float literal. public init(floatLiteral value: Double) { self.init(String(value), Tag(.float)) } } extension Node: ExpressibleByIntegerLiteral { + /// Create a `Node.scalar` from an integer literal. public init(integerLiteral value: Int) { self.init(String(value), Tag(.int)) } } extension Node: ExpressibleByStringLiteral { + /// Create a `Node.scalar` from a string literal. public init(stringLiteral value: String) { self.init(value) } From b15f1bd0b9c92295644eecba12a1b9b8e2d06b0d Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 16:40:20 -0700 Subject: [PATCH 15/35] Add docstrings to YamlError.swift --- Sources/Yams/YamlError.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Yams/YamlError.swift b/Sources/Yams/YamlError.swift index 54ec235..1a7d880 100644 --- a/Sources/Yams/YamlError.swift +++ b/Sources/Yams/YamlError.swift @@ -11,6 +11,7 @@ import CYaml #endif import Foundation +/// Errors thrown by Yams APIs. public enum YamlError: Swift.Error { // Used in `yaml_emitter_t` and `yaml_parser_t` /// `YAML_NO_ERROR`. No error is produced. @@ -69,7 +70,7 @@ public enum YamlError: Swift.Error { /// The error context public struct Context: CustomStringConvertible { - /// error context + /// context text public let text: String /// context position public let mark: Mark From 4c0ec3aa2077e9fc2ec301117cfacc5f9734ee26 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 16:43:32 -0700 Subject: [PATCH 16/35] Fix typos --- Sources/Yams/Representer.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Yams/Representer.swift b/Sources/Yams/Representer.swift index 0976f6b..299be42 100644 --- a/Sources/Yams/Representer.swift +++ b/Sources/Yams/Representer.swift @@ -21,7 +21,7 @@ public extension Node { } // MARK: - NodeRepresentable -/// Type is representabe as `Node` +/// Type is representable as `Node` public protocol NodeRepresentable { func represented() throws -> Node } @@ -56,7 +56,7 @@ private func represent(_ value: Any) throws -> Node { } // MARK: - ScalarRepresentable -/// Type is representabe as `Node.scalar` +/// Type is representable as `Node.scalar` public protocol ScalarRepresentable: NodeRepresentable { func represented() -> Node.Scalar } From 2f3fe06fff2fba954278b72fa9900f7893ac3f31 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 16:49:40 -0700 Subject: [PATCH 17/35] Add docstrings to Representer.swift --- Sources/Yams/Representer.swift | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/Sources/Yams/Representer.swift b/Sources/Yams/Representer.swift index 299be42..6bdb3ff 100644 --- a/Sources/Yams/Representer.swift +++ b/Sources/Yams/Representer.swift @@ -12,27 +12,32 @@ import CYaml import Foundation public extension Node { - /// initialize `Node` with instance of `NodeRepresentable` - /// - Parameter representable: instance of `NodeRepresentable` - /// - Throws: `YamlError` + /// Initialize a `Node` with a value of `NodeRepresentable`. + /// + /// - parameter representable: Value of `NodeRepresentable` to represent as a `Node`. + /// + /// - throws: `YamlError`. public init(_ representable: T) throws { self = try representable.represented() } } // MARK: - NodeRepresentable -/// Type is representable as `Node` +/// Type is representable as `Node`. public protocol NodeRepresentable { + /// This value's `Node` representation. func represented() throws -> Node } extension Node: NodeRepresentable { + /// This value's `Node` representation. public func represented() throws -> Node { return self } } extension Array: NodeRepresentable { + /// This value's `Node` representation. public func represented() throws -> Node { let nodes = try map(represent) return Node(nodes, Tag(.seq)) @@ -40,6 +45,7 @@ extension Array: NodeRepresentable { } extension Dictionary: NodeRepresentable { + /// This value's `Node` representation. public func represented() throws -> Node { let pairs = try map { (key: try represent($0.0), value: try represent($0.1)) } return Node(pairs.sorted { $0.key < $1.key }, Tag(.map)) @@ -56,30 +62,35 @@ private func represent(_ value: Any) throws -> Node { } // MARK: - ScalarRepresentable -/// Type is representable as `Node.scalar` +/// Type is representable as `Node.scalar`. public protocol ScalarRepresentable: NodeRepresentable { + /// This value's `Node.scalar` representation. func represented() -> Node.Scalar } extension ScalarRepresentable { + /// This value's `Node.scalar` representation. public func represented() throws -> Node { return .scalar(represented()) } } extension Bool: ScalarRepresentable { + /// This value's `Node.scalar` representation. public func represented() -> Node.Scalar { return .init(self ? "true" : "false", Tag(.bool)) } } 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)) } @@ -141,12 +152,14 @@ private let iso8601WithFractionalSecondFormatter: DateFormatter = { }() 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)) } } 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)) } @@ -172,6 +185,7 @@ private let floatFormatter = numberFormatter(with: 7) //extension Float80: ScalarRepresentable {} extension BinaryInteger { + /// This value's `Node.scalar` representation. public func represented() -> Node.Scalar { return .init(String(describing: self), Tag(.int)) } @@ -189,6 +203,7 @@ extension UInt64: ScalarRepresentable {} extension UInt8: ScalarRepresentable {} extension Optional: NodeRepresentable { + /// This value's `Node.scalar` representation. public func represented() throws -> Node { switch self { case let .some(wrapped): @@ -200,18 +215,21 @@ 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 { return .init(self) } @@ -219,11 +237,14 @@ extension String: ScalarRepresentable { /// MARK: - ScalarRepresentableCustomizedForCodable +/// Types conforming to this protocol can be encoded by `YamlEncoder`. public protocol YAMLEncodable: Encodable { + /// Returns this value wrapped in a `Node`. func box() -> Node } extension YAMLEncodable where Self: ScalarRepresentable { + /// Returns this value wrapped in a `Node.scalar`. public func box() -> Node { return .scalar(represented()) } @@ -246,18 +267,21 @@ extension URL: YAMLEncodable {} extension String: 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)) } } extension Float: YAMLEncodable { + /// Returns this value wrapped in a `Node.scalar`. public func box() -> Node { return Node(formattedStringForCodable, Tag(.float)) } From 42b94088a0098c8ac9c6c1392cbbdd51bae823e7 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 17:00:56 -0700 Subject: [PATCH 18/35] Add docstrings to Constructor.swift --- Sources/Yams/Constructor.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Sources/Yams/Constructor.swift b/Sources/Yams/Constructor.swift index 512efa9..80658b4 100644 --- a/Sources/Yams/Constructor.swift +++ b/Sources/Yams/Constructor.swift @@ -432,10 +432,21 @@ extension Set { // MARK: Sequence extension Array { + /// Construct an Array of `Any` from the specified `sequence`. + /// + /// - parameter sequence: Sequence to convert to `Array`. + /// + /// - returns: Array of `Any`. public static func construct_seq(from sequence: Node.Sequence) -> [Any] { return sequence.map(sequence.tag.constructor.any) } + + /// Construct an "O-map" (array of `(Any, Any)` tuples) from the specified `sequence`. + /// + /// - parameter sequence: Sequence to convert to `Array<(Any, Any)>`. + /// + /// - returns: Array of `(Any, Any)` tuples. public static func construct_omap(from sequence: Node.Sequence) -> [(Any, Any)] { // Note: we do not check for duplicate keys. return sequence.compactMap { subnode -> (Any, Any)? in @@ -445,6 +456,11 @@ extension Array { } } + /// Construct an array of `(Any, Any)` tuples from the specified `sequence`. + /// + /// - parameter sequence: Sequence to convert to `Array<(Any, Any)>`. + /// + /// - returns: Array of `(Any, Any)` tuples. public static func construct_pairs(from sequence: Node.Sequence) -> [(Any, Any)] { // Note: we do not check for duplicate keys. return sequence.compactMap { subnode -> (Any, Any)? in From ce43d9faade419753b102909053ec7f7a3dd96bc Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 17:10:47 -0700 Subject: [PATCH 19/35] Add docstrings to Parser.swift --- Sources/Yams/Parser.swift | 41 ++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/Sources/Yams/Parser.swift b/Sources/Yams/Parser.swift index a07ae15..869e562 100644 --- a/Sources/Yams/Parser.swift +++ b/Sources/Yams/Parser.swift @@ -18,7 +18,7 @@ import Foundation /// - yaml: String /// - resolver: Resolver /// - constructor: Constructor -/// - Returns: YamlSequence +/// - returns: YamlSequence /// - Throws: YamlError public func load_all(yaml: String, _ resolver: Resolver = .default, @@ -34,7 +34,7 @@ public func load_all(yaml: String, /// - yaml: String /// - resolver: Resolver /// - constructor: Constructor -/// - Returns: Any? +/// - returns: Any? /// - Throws: YamlError public func load(yaml: String, _ resolver: Resolver = .default, @@ -49,7 +49,7 @@ public func load(yaml: String, /// - yaml: String /// - resolver: Resolver /// - constructor: Constructor -/// - Returns: YamlSequence +/// - returns: YamlSequence /// - Throws: YamlError public func compose_all(yaml: String, _ resolver: Resolver = .default, @@ -65,7 +65,7 @@ public func compose_all(yaml: String, /// - yaml: String /// - resolver: Resolver /// - constructor: Constructor -/// - Returns: Node? +/// - returns: Node? /// - Throws: YamlError public func compose(yaml: String, _ resolver: Resolver = .default, @@ -73,10 +73,12 @@ public func compose(yaml: String, return try Parser(yaml: yaml, resolver: resolver, constructor: constructor).singleRoot() } -/// Sequence that holds error +/// Sequence that holds an error. public struct YamlSequence: Sequence, IteratorProtocol { + /// This sequence's error, if any. public private(set) var error: Swift.Error? + /// `Swift.Sequence.next()`. public mutating func next() -> T? { do { return try closure() @@ -93,18 +95,22 @@ public struct YamlSequence: Sequence, IteratorProtocol { private let closure: () throws -> T? } +/// Parses YAML strings. public final class Parser { - // MARK: public + /// YAML string. public let yaml: String + /// Resolver. public let resolver: Resolver + /// Constructor. public let constructor: Constructor /// Set up Parser. /// - /// - Parameter string: YAML - /// - Parameter resolver: Resolver - /// - Parameter constructor: Constructor - /// - Throws: YamlError + /// - parameter string: YAML string. + /// - parameter resolver: Resolver, `.default` if omitted. + /// - parameter constructor: Constructor, `.default` if omitted. + /// + /// - throws: `YamlError`. public init(yaml string: String, resolver: Resolver = .default, constructor: Constructor = .default) throws { @@ -139,13 +145,19 @@ public final class Parser { /// Parse next document and return root Node. /// - /// - Returns: next Node - /// - Throws: YamlError + /// - returns: next Node. + /// + /// - throws: `YamlError`. public func nextRoot() throws -> Node? { guard !streamEndProduced, try parse().type != YAML_STREAM_END_EVENT else { return nil } return try loadDocument() } + /// Parses the document expecting a single root Node and returns it. + /// + /// - returns: Single root Node. + /// + /// - throws: `YamlError`. public func singleRoot() throws -> Node? { guard !streamEndProduced, try parse().type != YAML_STREAM_END_EVENT else { return nil } let node = try loadDocument() @@ -161,7 +173,8 @@ public final class Parser { return node } - // MARK: private + // MARK: - Private Members + private var anchors = [String: Node]() private var parser = yaml_parser_t() #if USE_UTF8 @@ -171,7 +184,7 @@ public final class Parser { #endif } -// MARK: implementation details +// MARK: Implementation Details private extension Parser { private var streamEndProduced: Bool { return parser.stream_end_produced != 0 From 96a117eab2bb05b5d49ba520fa39f4e20ab961d1 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 17:18:41 -0700 Subject: [PATCH 20/35] Add doscstrings to Tag.swift --- Sources/Yams/Tag.swift | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Sources/Yams/Tag.swift b/Sources/Yams/Tag.swift index 75aaf1f..3b18beb 100644 --- a/Sources/Yams/Tag.swift +++ b/Sources/Yams/Tag.swift @@ -11,18 +11,28 @@ import CYaml #endif import Foundation +/// Tags describe the the _type_ of a Node. public final class Tag { + /// Tag name. public struct Name: RawRepresentable { + /// This `Tag.Name`'s raw string value. public let rawValue: String + /// Create a `Tag.Name` with a raw string value. public init(rawValue: String) { self.rawValue = rawValue } } + /// Shorthand accessor for `Tag(.implicit)`. public static var implicit: Tag { return Tag(.implicit) } + /// Create a `Tag` with the specified name, resolver and constructor. + /// + /// - parameter name: Tag name. + /// - parameter resolver: `Resolver` this tag should use, `.default` if omitted. + /// - parameter constructor: `Constructor` this tag should use, `.default` if omitted. public init(_ name: Name, _ resolver: Resolver = .default, _ constructor: Constructor = .default) { @@ -31,6 +41,15 @@ public final class Tag { self.name = name } + /// Lens returning a copy of the current `Tag` with the specified overridden changes. + /// + /// - note: Omitting or passing nil for a parameter will preserve the current `Tag`'s value in the copy. + /// + /// - parameter name: Overridden tag name. + /// - parameter resolver: Overridden resolver. + /// - parameter constructor: Overridden constructor. + /// + /// - returns: A copy of the current `Tag` with the specified overridden changes. public func copy(with name: Name? = nil, resolver: Resolver? = nil, constructor: Constructor? = nil) -> Tag { return .init(name ?? self.name, resolver ?? self.resolver, constructor ?? self.constructor) } @@ -53,32 +72,38 @@ public final class Tag { } extension Tag: CustomStringConvertible { + /// A textual representation of this tag. public var description: String { return name.rawValue } } extension Tag: Hashable { + /// :nodoc: public var hashValue: Int { return name.hashValue } + /// :nodoc: public static func == (lhs: Tag, rhs: Tag) -> Bool { return lhs.name == rhs.name } } extension Tag.Name: ExpressibleByStringLiteral { + /// :nodoc: public init(stringLiteral value: String) { self.rawValue = value } } extension Tag.Name: Hashable { + /// :nodoc: public var hashValue: Int { return rawValue.hashValue } + /// :nodoc: public static func == (lhs: Tag.Name, rhs: Tag.Name) -> Bool { return lhs.rawValue == rhs.rawValue } From e598f5c85390aeb5d47fcd61a3fad7e38f7f5a26 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 17:22:50 -0700 Subject: [PATCH 21/35] Allow nesting types 2 levels & add docstrings to Node.Mapping.swift --- .swiftlint.yml | 2 ++ Sources/Yams/Node.Mapping.swift | 6 +++++- Sources/Yams/Node.Scalar.swift | 2 +- Sources/Yams/Node.Sequence.swift | 2 +- Tests/YamsTests/EncoderTests.swift | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 12eaa85..9cf44d8 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -5,3 +5,5 @@ identifier_name: excluded: - cr - ln +nesting: + type_level: 2 diff --git a/Sources/Yams/Node.Mapping.swift b/Sources/Yams/Node.Mapping.swift index 57bdf8d..9ac06ec 100644 --- a/Sources/Yams/Node.Mapping.swift +++ b/Sources/Yams/Node.Mapping.swift @@ -9,13 +9,17 @@ import Foundation extension Node { + /// A mapping is the YAML equivalent of a `Dictionary`. public struct Mapping { private var pairs: [Pair] + /// This mapping's `Tag`. public var tag: Tag + /// This mapping's `Style`. public var style: Style + /// This mapping's `Mark`. public var mark: Mark? - public enum Style: UInt32 { // swiftlint:disable:this nesting + public enum Style: UInt32 { /// Let the emitter choose the style. case any /// The block mapping style. diff --git a/Sources/Yams/Node.Scalar.swift b/Sources/Yams/Node.Scalar.swift index 419079b..0c016b3 100644 --- a/Sources/Yams/Node.Scalar.swift +++ b/Sources/Yams/Node.Scalar.swift @@ -19,7 +19,7 @@ extension Node { public var style: Style public var mark: Mark? - public enum Style: UInt32 { // swiftlint:disable:this nesting + public enum Style: UInt32 { /// Let the emitter choose the style. case any = 0 /// The plain scalar style. diff --git a/Sources/Yams/Node.Sequence.swift b/Sources/Yams/Node.Sequence.swift index 723cbb6..787eb06 100644 --- a/Sources/Yams/Node.Sequence.swift +++ b/Sources/Yams/Node.Sequence.swift @@ -15,7 +15,7 @@ extension Node { public var style: Style public var mark: Mark? - public enum Style: UInt32 { // swiftlint:disable:this nesting + public enum Style: UInt32 { /// Let the emitter choose the style. case any /// The block sequence style. diff --git a/Tests/YamsTests/EncoderTests.swift b/Tests/YamsTests/EncoderTests.swift index 103c640..b6817f2 100644 --- a/Tests/YamsTests/EncoderTests.swift +++ b/Tests/YamsTests/EncoderTests.swift @@ -241,7 +241,7 @@ class EncoderTests: XCTestCase { // swiftlint:disable:this type_body_length func testNodeTypeMismatch() throws { // https://github.com/jpsim/Yams/pull/95 - struct Sample: Decodable { // swiftlint:disable:this nesting + struct Sample: Decodable { let values: [String] } From 35ca190308e27bd17210be7ca08ba45205a83647 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 17:38:54 -0700 Subject: [PATCH 22/35] More docstrings for Node.swift & Node.Mapping.swift --- Sources/Yams/Node.Mapping.swift | 38 ++++++++++++++++++++++++++++----- Sources/Yams/Node.swift | 4 ++-- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/Sources/Yams/Node.Mapping.swift b/Sources/Yams/Node.Mapping.swift index 9ac06ec..da95eee 100644 --- a/Sources/Yams/Node.Mapping.swift +++ b/Sources/Yams/Node.Mapping.swift @@ -14,11 +14,12 @@ extension Node { private var pairs: [Pair] /// This mapping's `Tag`. public var tag: Tag - /// This mapping's `Style`. + /// The style to use when emitting this `Mapping`. public var style: Style /// This mapping's `Mark`. public var mark: Mark? + /// The style to use when emitting a `Mapping`. public enum Style: UInt32 { /// Let the emitter choose the style. case any @@ -28,6 +29,12 @@ extension Node { case flow } + /// Create a `Node.Mapping` using the specified parameters. + /// + /// - parameter pairs: The array of `(Node, Node)` tuples to generate this mapping. + /// - parameter tag: This mapping's `Tag`. + /// - parameter style: The style to use when emitting this `Mapping`. + /// - parameter mark: This mapping's `Mark`. public init(_ pairs: [(Node, Node)], _ tag: Tag = .implicit, _ style: Style = .any, _ mark: Mark? = nil) { self.pairs = pairs.map { Pair($0.0, $0.1) } self.tag = tag @@ -36,6 +43,7 @@ extension Node { } } + /// Get or set the `Node.Mapping` value if this node is a `Node.mapping`. public var mapping: Mapping? { get { if case let .mapping(mapping) = self { @@ -52,47 +60,60 @@ extension Node { } extension Node.Mapping: Comparable { + /// :nodoc: public static func < (lhs: Node.Mapping, rhs: Node.Mapping) -> Bool { return lhs.pairs < rhs.pairs } } extension Node.Mapping: Equatable { + /// :nodoc: public static func == (lhs: Node.Mapping, rhs: Node.Mapping) -> Bool { return lhs.pairs == rhs.pairs && lhs.resolvedTag == rhs.resolvedTag } } extension Node.Mapping: ExpressibleByDictionaryLiteral { + /// :nodoc: public init(dictionaryLiteral elements: (Node, Node)...) { self.init(elements) } } +// MARK: - MutableCollection Conformance + extension Node.Mapping: MutableCollection { + /// :nodoc: public typealias Element = (key: Node, value: Node) - // Sequence + // MARK: Sequence + + /// :nodoc: public func makeIterator() -> Array.Iterator { - let iterator = pairs.map(Pair.toTuple).makeIterator() - return iterator + return pairs.map(Pair.toTuple).makeIterator() } - // Collection + // MARK: Collection + + /// The index type for this mapping. public typealias Index = Array.Index + /// :nodoc: public var startIndex: Index { return pairs.startIndex } + /// :nodoc: public var endIndex: Index { return pairs.endIndex } + /// :nodoc: public func index(after index: Index) -> Index { return pairs.index(after: index) } + /// :nodoc: public subscript(index: Index) -> Element { get { return (key: pairs[index].key, value: pairs[index].value) @@ -108,15 +129,20 @@ extension Node.Mapping: TagResolvable { static let defaultTagName = Tag.Name.map } +// MARK: - Dictionary-like APIs + extension Node.Mapping { + /// This mapping's keys. Similar to `Dictionary.keys`. public var keys: LazyMapCollection { return lazy.map { $0.key } } + /// This mapping's keys. Similar to `Dictionary.keys`. public var values: LazyMapCollection { return lazy.map { $0.value } } + /// Set or get the `Node` for the specified string's `Node` representation. public subscript(string: String) -> Node? { get { return self[Node(string, tag.copy(with: .implicit))] @@ -126,6 +152,7 @@ extension Node.Mapping { } } + /// Set or get the specified `Node`. public subscript(node: Node) -> Node? { get { let v = pairs.reversed().first(where: { $0.key == node }) @@ -146,6 +173,7 @@ extension Node.Mapping { } } + /// Get the index of the specified `Node`, if it exists in the mapping. public func index(forKey key: Node) -> Index? { return pairs.reversed().index(where: { $0.key == key }).map({ pairs.index(before: $0.base) }) } diff --git a/Sources/Yams/Node.swift b/Sources/Yams/Node.swift index 2d54f9a..c80585d 100644 --- a/Sources/Yams/Node.swift +++ b/Sources/Yams/Node.swift @@ -120,7 +120,7 @@ extension Node { return sequence.map(Array.init) ?? [] } - /// Typed Array using type parameter: e.g. `array(of: String.self)` + /// Typed Array using type parameter: e.g. `array(of: String.self)`. /// /// - parameter type: Type conforming to `ScalarConstructible`. /// @@ -129,7 +129,7 @@ extension Node { return sequence?.compactMap { $0.scalar.flatMap(type.construct) } ?? [] } - /// If the node is a `.sequence` or `.mapping`, set or get the specified node. + /// If the node is a `.sequence` or `.mapping`, set or get the specified `Node`. /// If the node is a `.scalar`, this is a no-op. public subscript(node: Node) -> Node? { get { From faa2541f53610b0b58274fb6c8787842b7b29fd8 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 29 Apr 2018 17:43:46 -0700 Subject: [PATCH 23/35] Fix SwiftLint violation --- Sources/Yams/Constructor.swift | 1 - Sources/Yams/Node.Mapping.swift | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Sources/Yams/Constructor.swift b/Sources/Yams/Constructor.swift index 80658b4..0a04480 100644 --- a/Sources/Yams/Constructor.swift +++ b/Sources/Yams/Constructor.swift @@ -441,7 +441,6 @@ extension Array { return sequence.map(sequence.tag.constructor.any) } - /// Construct an "O-map" (array of `(Any, Any)` tuples) from the specified `sequence`. /// /// - parameter sequence: Sequence to convert to `Array<(Any, Any)>`. diff --git a/Sources/Yams/Node.Mapping.swift b/Sources/Yams/Node.Mapping.swift index da95eee..94b7386 100644 --- a/Sources/Yams/Node.Mapping.swift +++ b/Sources/Yams/Node.Mapping.swift @@ -155,8 +155,7 @@ extension Node.Mapping { /// Set or get the specified `Node`. public subscript(node: Node) -> Node? { get { - let v = pairs.reversed().first(where: { $0.key == node }) - return v?.value + return pairs.reversed().first(where: { $0.key == node })?.value } set { if let newValue = newValue { From 3708bcb0a4fd362baed3048335e64fc93d1bb643 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 6 May 2018 11:03:55 -0700 Subject: [PATCH 24/35] Add docstrings for Node.Scalar.swift --- Sources/Yams/Node.Scalar.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Sources/Yams/Node.Scalar.swift b/Sources/Yams/Node.Scalar.swift index 0c016b3..c2eacea 100644 --- a/Sources/Yams/Node.Scalar.swift +++ b/Sources/Yams/Node.Scalar.swift @@ -8,17 +8,25 @@ import Foundation +// MARK: Node+Scalar + extension Node { + /// Scalar node. public struct Scalar { + /// This node's string value. public var string: String { didSet { tag = .implicit } } + /// This node's tag (its type). public var tag: Tag + /// The style to be used when emitting this node. public var style: Style + /// The location for this node. public var mark: Mark? + /// The style to use when emitting a `Scalar`. public enum Style: UInt32 { /// Let the emitter choose the style. case any = 0 @@ -36,6 +44,12 @@ extension Node { case folded } + /// Create a `Node.Scalar` using the specified parameters. + /// + /// - parameter string: The string to generate this scalar. + /// - parameter tag: This scalar's `Tag`. + /// - parameter style: The style to use when emitting this `Scalar`. + /// - parameter mark: This scalar's `Mark`. public init(_ string: String, _ tag: Tag = .implicit, _ style: Style = .any, _ mark: Mark? = nil) { self.string = string self.tag = tag @@ -44,6 +58,7 @@ extension Node { } } + /// Get or set the `Node.Scalar` value if this node is a `Node.scalar`. public var scalar: Scalar? { get { if case let .scalar(scalar) = self { @@ -60,12 +75,14 @@ extension Node { } extension Node.Scalar: Comparable { + /// :nodoc: public static func < (lhs: Node.Scalar, rhs: Node.Scalar) -> Bool { return lhs.string < rhs.string } } extension Node.Scalar: Equatable { + /// :nodoc: public static func == (lhs: Node.Scalar, rhs: Node.Scalar) -> Bool { return lhs.string == rhs.string && lhs.resolvedTag == rhs.resolvedTag } From 9bbd3a2c40f413eb4225fe2992e17ef7c38cd38a Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 6 May 2018 11:07:16 -0700 Subject: [PATCH 25/35] Add docstrings to Node.Sequence.swift --- Sources/Yams/Node.Sequence.swift | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Sources/Yams/Node.Sequence.swift b/Sources/Yams/Node.Sequence.swift index 787eb06..f9ef9de 100644 --- a/Sources/Yams/Node.Sequence.swift +++ b/Sources/Yams/Node.Sequence.swift @@ -8,13 +8,20 @@ import Foundation +// MARK: Node+Sequence + extension Node { + /// Sequence node. public struct Sequence { private var nodes: [Node] + /// This node's tag (its type). public var tag: Tag + /// The style to be used when emitting this node. public var style: Style + /// The location for this node. public var mark: Mark? + /// The style to use when emitting a `Sequence`. public enum Style: UInt32 { /// Let the emitter choose the style. case any @@ -24,6 +31,12 @@ extension Node { case flow } + /// Create a `Node.Sequence` using the specified parameters. + /// + /// - parameter nodes: The array of nodes to generate this sequence. + /// - parameter tag: This sequence's `Tag`. + /// - parameter style: The style to use when emitting this `Sequence`. + /// - parameter mark: This sequence's `Mark`. public init(_ nodes: [Node], _ tag: Tag = .implicit, _ style: Style = .any, _ mark: Mark? = nil) { self.nodes = nodes self.tag = tag @@ -32,6 +45,7 @@ extension Node { } } + /// Get or set the `Node.Sequence` value if this node is a `Node.sequence`. public var sequence: Sequence? { get { if case let .sequence(sequence) = self { @@ -50,18 +64,21 @@ extension Node { // MARK: - Node.Sequence extension Node.Sequence: Comparable { + /// :nodoc: public static func < (lhs: Node.Sequence, rhs: Node.Sequence) -> Bool { return lhs.nodes < rhs.nodes } } extension Node.Sequence: Equatable { + /// :nodoc: public static func == (lhs: Node.Sequence, rhs: Node.Sequence) -> Bool { return lhs.nodes == rhs.nodes && lhs.resolvedTag == rhs.resolvedTag } } extension Node.Sequence: ExpressibleByArrayLiteral { + /// :nodoc: public init(arrayLiteral elements: Node...) { self.init(elements) } @@ -69,25 +86,31 @@ extension Node.Sequence: ExpressibleByArrayLiteral { extension Node.Sequence: MutableCollection { // Sequence + /// :nodoc: public func makeIterator() -> Array.Iterator { return nodes.makeIterator() } // Collection + /// :nodoc: public typealias Index = Array.Index + /// :nodoc: public var startIndex: Index { return nodes.startIndex } + /// :nodoc: public var endIndex: Index { return nodes.endIndex } + /// :nodoc: public func index(after index: Index) -> Index { return nodes.index(after: index) } + /// :nodoc: public subscript(index: Index) -> Node { get { return nodes[index] @@ -98,6 +121,7 @@ extension Node.Sequence: MutableCollection { } } + /// :nodoc: public subscript(bounds: Range) -> Array.SubSequence { get { return nodes[bounds] @@ -108,6 +132,7 @@ extension Node.Sequence: MutableCollection { } } + /// :nodoc: public var indices: Array.Indices { return nodes.indices } @@ -115,25 +140,30 @@ extension Node.Sequence: MutableCollection { extension Node.Sequence: RandomAccessCollection { // BidirectionalCollection + /// :nodoc: public func index(before index: Index) -> Index { return nodes.index(before: index) } // RandomAccessCollection + /// :nodoc: public func index(_ index: Index, offsetBy num: Int) -> Index { return nodes.index(index, offsetBy: num) } + /// :nodoc: public func distance(from start: Index, to end: Int) -> Index { return nodes.distance(from: start, to: end) } } extension Node.Sequence: RangeReplaceableCollection { + /// :nodoc: public init() { self.init([]) } + /// :nodoc: public mutating func replaceSubrange(_ subrange: Range, with newElements: C) where C: Collection, C.Iterator.Element == Node { nodes.replaceSubrange(subrange, with: newElements) From f761dda3ef70814c489997e400e648a4a87551e1 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 6 May 2018 11:14:26 -0700 Subject: [PATCH 26/35] Add docstrings to Resolver.swift --- Sources/Yams/Resolver.swift | 50 ++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/Sources/Yams/Resolver.swift b/Sources/Yams/Resolver.swift index 95852e7..3450987 100644 --- a/Sources/Yams/Resolver.swift +++ b/Sources/Yams/Resolver.swift @@ -8,22 +8,38 @@ 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 - let regexp: NSRegularExpression + internal let regexp: NSRegularExpression + /// The regex pattern used to resolve this rule. public var pattern: String { return regexp.pattern } + /// Create a rule with the specified tag name and regex pattern. + /// + /// - parameter tag: The tag name this rule should apply to. + /// - parameter pattern: The regex pattern used to resolve this rule. + /// + /// - 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: []) } } + /// The rules used by this resolver to resolve nodes to tags. public let rules: [Rule] - init(_ rules: [Rule] = []) { self.rules = rules } + internal init(_ rules: [Rule] = []) { self.rules = rules } + /// Resolve a tag name from a given node. + /// + /// - parameter node: Node whose tag should be resolved. + /// + /// - returns: The resolved tag name. public func resolveTag(of node: Node) -> Tag.Name { switch node { case let .scalar(scalar): @@ -74,19 +90,28 @@ public final class Resolver { } } +// MARK: Defaults + extension Resolver { + /// Resolver with no rules. public static let basic = Resolver() + /// Resolver with a default set of rules rules. public static let `default` = Resolver([.bool, .int, .float, .merge, .null, .timestamp, .value]) } +// MARK: Default Resolver Rules + extension Resolver.Rule { - // swiftlint:disable:next force_try + // swiftlint:disable force_try + + /// Default bool resolver rule. public static let bool = try! Resolver.Rule(.bool, """ ^(?:yes|Yes|YES|no|No|NO\ |true|True|TRUE|false|False|FALSE\ |on|On|ON|off|Off|OFF)$ """) - // swiftlint:disable:next force_try + + /// Default int resolver rule. public static let int = try! Resolver.Rule(.int, """ ^(?:[-+]?0b[0-1_]+\ |[-+]?0o?[0-7_]+\ @@ -94,7 +119,8 @@ extension Resolver.Rule { |[-+]?0x[0-9a-fA-F_]+\ |[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$ """) - // swiftlint:disable:next force_try + + /// Default float resolver rule. public static let float = try! Resolver.Rule(.float, """ ^(?:[-+]?(?:[0-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?\ |\\.[0-9_]+(?:[eE][-+][0-9]+)?\ @@ -102,15 +128,18 @@ extension Resolver.Rule { |[-+]?\\.(?:inf|Inf|INF)\ |\\.(?:nan|NaN|NAN))$ """) - // swiftlint:disable:next force_try + + /// Default merge resolver rule. public static let merge = try! Resolver.Rule(.merge, "^(?:<<)$") - // swiftlint:disable:next force_try + + /// Default null resolver rule. public static let null = try! Resolver.Rule(.null, """ ^(?:~\ |null|Null|NULL\ |)$ """) - // swiftlint:disable:next force_try + + /// Default timestamp resolver rule. public static let timestamp = try! Resolver.Rule(.timestamp, """ ^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\ |[0-9][0-9][0-9][0-9]-[0-9][0-9]?-[0-9][0-9]?\ @@ -118,8 +147,11 @@ extension Resolver.Rule { :[0-9][0-9]:[0-9][0-9](?:\\.[0-9]*)?\ (?:[ \\t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$ """) - // swiftlint:disable:next force_try + + /// Default value resolver rule. public static let value = try! Resolver.Rule(.value, "^(?:=)$") + + // swiftlint:enable force_try } func pattern(_ string: String) -> NSRegularExpression { From ac1626fce0acddf4254acf874abc72c7f04f0668 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 6 May 2018 11:26:49 -0700 Subject: [PATCH 27/35] Add jazzy to Gemfile --- Gemfile | 1 + Gemfile.lock | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 00a6b3f..fb8bd60 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,4 @@ source 'https://rubygems.org' gem 'cocoapods' +gem "jazzy" diff --git a/Gemfile.lock b/Gemfile.lock index af61694..f79069f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -45,20 +45,46 @@ GEM colored2 (3.1.2) concurrent-ruby (1.0.5) escape (0.0.4) + ffi (1.9.23) fourflusher (2.0.1) fuzzy_match (2.0.4) gh_inspector (1.1.2) i18n (0.9.5) concurrent-ruby (~> 1.0) + jazzy (0.9.2) + cocoapods (~> 1.0) + mustache (~> 0.99) + open4 + redcarpet (~> 3.2) + rouge (>= 2.0.6, < 4.0) + sass (~> 3.4) + sqlite3 (~> 1.3) + xcinvoke (~> 0.3.0) + liferaft (0.0.6) minitest (5.11.3) molinillo (0.6.4) + mustache (0.99.8) nanaimo (0.2.3) nap (1.1.0) netrc (0.11.0) + open4 (1.3.4) + rb-fsevent (0.10.3) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + redcarpet (3.4.0) + rouge (3.1.1) ruby-macho (1.1.0) + sass (3.5.6) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sqlite3 (1.3.13) thread_safe (0.3.6) tzinfo (1.2.5) thread_safe (~> 0.1) + xcinvoke (0.3.0) + liferaft (~> 0.0.6) xcodeproj (1.5.6) CFPropertyList (~> 2.3.3) atomos (~> 0.1.2) @@ -71,6 +97,7 @@ PLATFORMS DEPENDENCIES cocoapods + jazzy BUNDLED WITH - 1.15.4 + 1.16.0 From 47220b97acb3bf0933f390a7a52d0a1d2637ce9e Mon Sep 17 00:00:00 2001 From: JP Simard Date: Sun, 6 May 2018 11:27:27 -0700 Subject: [PATCH 28/35] Add jazzy job to CircleCI --- .circleci/config.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 331bd62..97e5742 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,6 +23,30 @@ aliases: - vendor/bundle - run: bundle exec pod lib lint + - &steps-for-jazzy + - checkout + - run: echo "ruby-2.3" > ~/.ruby-version + - restore_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + - run: | + ( + export ARCHFLAGS="-arch x86_64" + bundle check || bundle install --path vendor/bundle + ) + - save_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + paths: + - vendor/bundle + - run: bundle exec jazzy + - store_artifacts: + path: docs + - run: | + if ruby -rjson -e "j = JSON.parse(File.read('docs/undocumented.json')); exit j['warnings'].length != 0"; then + echo "Undocumented declarations:" + cat "$undocumented" + exit 1 + fi + - &steps-for-spm - checkout - run: swift test @@ -60,6 +84,11 @@ jobs: xcode: "9.3.0" << : *XCODE9 + jazzy: + macos: + xcode: "9.3.0" + steps: *steps-for-jazzy + spm_swift_4: macos: xcode: "9.0" @@ -108,6 +137,7 @@ workflows: - xcode_9.1_swift_4.0.2 - xcode_9.2_swift_4.0.3 - xcode_9.3_swift_4.1 + - jazzy - spm_swift_4 - spm_swift_4.0.2 - spm_swift_4.0.3 From ebf789366c21b741797241c5057507d51bf335f6 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Wed, 9 May 2018 16:32:35 -0700 Subject: [PATCH 29/35] Add docstrings for sortKeys --- Sources/Yams/Emitter.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/Yams/Emitter.swift b/Sources/Yams/Emitter.swift index d1ef49b..becde2d 100644 --- a/Sources/Yams/Emitter.swift +++ b/Sources/Yams/Emitter.swift @@ -22,6 +22,7 @@ import Foundation /// - parameter explicitStart: Explicit document start `---`. /// - parameter explicitEnd: Explicit document end `...`. /// - parameter version: YAML version directive. +/// - parameter sortKeys: Whether or not to sort Mapping keys in lexicographic order. /// /// - returns: YAML string. /// @@ -70,6 +71,7 @@ public func dump( /// - parameter explicitStart: Explicit document start `---`. /// - parameter explicitEnd: Explicit document end `...`. /// - parameter version: YAML version directive. +/// - parameter sortKeys: Whether or not to sort Mapping keys in lexicographic order. /// /// - returns: YAML string. /// @@ -110,6 +112,7 @@ public func dump( /// - parameter explicitStart: Explicit document start `---`. /// - parameter explicitEnd: Explicit document end `...`. /// - parameter version: YAML version directive. +/// - parameter sortKeys: Whether or not to sort Mapping keys in lexicographic order. /// /// - returns: YAML string. /// @@ -158,6 +161,7 @@ public func serialize( /// - parameter explicitStart: Explicit document start `---`. /// - parameter explicitEnd: Explicit document end `...`. /// - parameter version: YAML version directive. +/// - parameter sortKeys: Whether or not to sort Mapping keys in lexicographic order. /// /// - returns: YAML string. /// From f540796d7279918591faa42792e0e9ffb7887a77 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Wed, 9 May 2018 16:33:34 -0700 Subject: [PATCH 30/35] Remove extra word --- Sources/Yams/Resolver.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Yams/Resolver.swift b/Sources/Yams/Resolver.swift index 3450987..193df89 100644 --- a/Sources/Yams/Resolver.swift +++ b/Sources/Yams/Resolver.swift @@ -95,7 +95,7 @@ public final class Resolver { extension Resolver { /// Resolver with no rules. public static let basic = Resolver() - /// Resolver with a default set of rules rules. + /// Resolver with a default set of rules. public static let `default` = Resolver([.bool, .int, .float, .merge, .null, .timestamp, .value]) } From 0d3f24226e22d78b04426a244191bf64a450ea5c Mon Sep 17 00:00:00 2001 From: JP Simard Date: Wed, 9 May 2018 16:34:56 -0700 Subject: [PATCH 31/35] Make Emitter.data publicly readable --- Sources/Yams/Emitter.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Yams/Emitter.swift b/Sources/Yams/Emitter.swift index becde2d..2dbd3b9 100644 --- a/Sources/Yams/Emitter.swift +++ b/Sources/Yams/Emitter.swift @@ -203,7 +203,8 @@ public final class Emitter { case crln } - internal var data = Data() + /// Retrieve this Emitter's binary output. + public internal(set) var data = Data() /// Configuration options to use when emitting YAML. public struct Options { From 9d8a174ec530910f453004866bc79981da902d6f Mon Sep 17 00:00:00 2001 From: JP Simard Date: Wed, 9 May 2018 16:35:52 -0700 Subject: [PATCH 32/35] Update CocoaPods to 1.5.2 & jazzy to 0.9.3 --- Gemfile.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f79069f..9167f8d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (2.3.6) + CFPropertyList (3.0.0) activesupport (4.2.10) i18n (~> 0.7) minitest (~> 5.1) @@ -9,12 +9,12 @@ GEM tzinfo (~> 1.1) atomos (0.1.2) claide (1.0.2) - cocoapods (1.4.0) + cocoapods (1.5.2) activesupport (>= 4.0.2, < 5) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.4.0) + cocoapods-core (= 1.5.2) cocoapods-deintegrate (>= 1.0.2, < 2.0) - cocoapods-downloader (>= 1.1.3, < 2.0) + cocoapods-downloader (>= 1.2.0, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) cocoapods-search (>= 1.0.0, < 2.0) cocoapods-stats (>= 1.0.0, < 2.0) @@ -24,16 +24,16 @@ GEM escape (~> 0.0.4) fourflusher (~> 2.0.1) gh_inspector (~> 1.0) - molinillo (~> 0.6.4) + molinillo (~> 0.6.5) nap (~> 1.0) ruby-macho (~> 1.1) - xcodeproj (>= 1.5.4, < 2.0) - cocoapods-core (1.4.0) + xcodeproj (>= 1.5.7, < 2.0) + cocoapods-core (1.5.2) activesupport (>= 4.0.2, < 6) fuzzy_match (~> 2.0.4) nap (~> 1.0) cocoapods-deintegrate (1.0.2) - cocoapods-downloader (1.1.3) + cocoapods-downloader (1.2.0) cocoapods-plugins (1.0.0) nap cocoapods-search (1.0.0) @@ -48,10 +48,10 @@ GEM ffi (1.9.23) fourflusher (2.0.1) fuzzy_match (2.0.4) - gh_inspector (1.1.2) + gh_inspector (1.1.3) i18n (0.9.5) concurrent-ruby (~> 1.0) - jazzy (0.9.2) + jazzy (0.9.3) cocoapods (~> 1.0) mustache (~> 0.99) open4 @@ -62,9 +62,9 @@ GEM xcinvoke (~> 0.3.0) liferaft (0.0.6) minitest (5.11.3) - molinillo (0.6.4) + molinillo (0.6.5) mustache (0.99.8) - nanaimo (0.2.3) + nanaimo (0.2.5) nap (1.1.0) netrc (0.11.0) open4 (1.3.4) @@ -85,12 +85,12 @@ GEM thread_safe (~> 0.1) xcinvoke (0.3.0) liferaft (~> 0.0.6) - xcodeproj (1.5.6) - CFPropertyList (~> 2.3.3) + xcodeproj (1.5.8) + CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.2) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) - nanaimo (~> 0.2.3) + nanaimo (~> 0.2.5) PLATFORMS ruby From 44a6a3b07837e8666f6937902a5805b9a45ac011 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Wed, 9 May 2018 19:56:18 -0700 Subject: [PATCH 33/35] Hardcode absolute URL for yams.jpg --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5da29d7..01de6b8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Yams -![Yams](yams.jpg) +![Yams](https://raw.githubusercontent.com/jpsim/Yams/master/yams.jpg) A sweet and swifty [Yaml](http://yaml.org/) parser built on [libYAML](http://pyyaml.org/wiki/LibYAML). From 1b4531e616945999916a640e1f5554acd5e53eb3 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Wed, 9 May 2018 19:59:27 -0700 Subject: [PATCH 34/35] Update names & URLs --- .jazzy.yaml | 5 +++-- README.md | 4 ++-- Yams.podspec | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.jazzy.yaml b/.jazzy.yaml index 53ede41..2109257 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -1,8 +1,9 @@ module: Yams author: JP Simard, Norio Nomura -author_url: http://jpsim.com +author_url: https://jpsim.com +root_url: https://jpsim.com/Yams/ github_url: https://github.com/jpsim/Yams github_file_prefix: https://github.com/jpsim/Yams/tree/docs theme: fullwidth clean: true -copyright: '© 2018 [JP Simard](http://jpsim.com) under MIT.' +copyright: '© 2018 [JP Simard](https://jpsim.com) under MIT.' diff --git a/README.md b/README.md index 01de6b8..ce083d2 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ ![Yams](https://raw.githubusercontent.com/jpsim/Yams/master/yams.jpg) -A sweet and swifty [Yaml](http://yaml.org/) parser built on -[libYAML](http://pyyaml.org/wiki/LibYAML). +A sweet and swifty [YAML](http://yaml.org/) parser built on +[LibYAML](https://github.com/yaml/libyaml). [![CircleCI](https://circleci.com/gh/jpsim/Yams.svg?style=svg)](https://circleci.com/gh/jpsim/Yams) diff --git a/Yams.podspec b/Yams.podspec index ae6938c..c1bbba9 100644 --- a/Yams.podspec +++ b/Yams.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = 'Yams' s.version = '0.7.0' - s.summary = 'A sweet and swifty Yaml parser.' + s.summary = 'A sweet and swifty YAML parser.' s.homepage = 'https://github.com/jpsim/Yams' s.source = { :git => s.homepage + '.git', :tag => s.version } s.license = { :type => 'MIT', :file => 'LICENSE' } From 313b68d8dd2da633361c8dbaff30732f9febbcb4 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Wed, 9 May 2018 20:19:44 -0700 Subject: [PATCH 35/35] Docs formatting cleanup for consistency --- .swiftlint.yml | 1 + Sources/Yams/Encoder.swift | 4 +-- Sources/Yams/Parser.swift | 44 +++++++++++++----------- Sources/Yams/String+Yams.swift | 20 ++++++----- Sources/Yams/YamlError.swift | 58 +++++++++++++++++--------------- Tests/YamsTests/TestHelper.swift | 16 ++++----- 6 files changed, 77 insertions(+), 66 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 9cf44d8..18dee43 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -5,5 +5,6 @@ identifier_name: excluded: - cr - ln + - no nesting: type_level: 2 diff --git a/Sources/Yams/Encoder.swift b/Sources/Yams/Encoder.swift index c7cbda1..1f10cfe 100644 --- a/Sources/Yams/Encoder.swift +++ b/Sources/Yams/Encoder.swift @@ -22,8 +22,8 @@ public class YAMLEncoder { /// Encode a value of type `T` to a YAML string. /// - /// - value: Value to encode. - /// - userInfo: Additional key/values which can be used when looking up keys to encode. + /// - parameter value: Value to encode. + /// - parameter userInfo: Additional key/values which can be used when looking up keys to encode. /// /// - returns: The YAML string. /// diff --git a/Sources/Yams/Parser.swift b/Sources/Yams/Parser.swift index 869e562..557fe24 100644 --- a/Sources/Yams/Parser.swift +++ b/Sources/Yams/Parser.swift @@ -14,12 +14,13 @@ import Foundation /// Parse all YAML documents in a String /// and produce corresponding Swift objects. /// -/// - Parameters: -/// - yaml: String -/// - resolver: Resolver -/// - constructor: Constructor +/// - parameter yaml: String +/// - parameter resolver: Resolver +/// - parameter constructor: Constructor +/// /// - returns: YamlSequence -/// - Throws: YamlError +/// +/// - throws: YamlError public func load_all(yaml: String, _ resolver: Resolver = .default, _ constructor: Constructor = .default) throws -> YamlSequence { @@ -30,12 +31,13 @@ public func load_all(yaml: String, /// Parse the first YAML document in a String /// and produce the corresponding Swift object. /// -/// - Parameters: -/// - yaml: String -/// - resolver: Resolver -/// - constructor: Constructor +/// - parameter yaml: String +/// - parameter resolver: Resolver +/// - parameter constructor: Constructor +/// /// - returns: Any? -/// - Throws: YamlError +/// +/// - throws: YamlError public func load(yaml: String, _ resolver: Resolver = .default, _ constructor: Constructor = .default) throws -> Any? { @@ -45,12 +47,13 @@ public func load(yaml: String, /// Parse all YAML documents in a String /// and produce corresponding representation trees. /// -/// - Parameters: -/// - yaml: String -/// - resolver: Resolver -/// - constructor: Constructor +/// - parameter yaml: String +/// - parameter resolver: Resolver +/// - parameter constructor: Constructor +/// /// - returns: YamlSequence -/// - Throws: YamlError +/// +/// - throws: YamlError public func compose_all(yaml: String, _ resolver: Resolver = .default, _ constructor: Constructor = .default) throws -> YamlSequence { @@ -61,12 +64,13 @@ public func compose_all(yaml: String, /// Parse the first YAML document in a String /// and produce the corresponding representation tree. /// -/// - Parameters: -/// - yaml: String -/// - resolver: Resolver -/// - constructor: Constructor +/// - parameter yaml: String +/// - parameter resolver: Resolver +/// - parameter constructor: Constructor +/// /// - returns: Node? -/// - Throws: YamlError +/// +/// - throws: YamlError public func compose(yaml: String, _ resolver: Resolver = .default, _ constructor: Constructor = .default) throws -> Node? { diff --git a/Sources/Yams/String+Yams.swift b/Sources/Yams/String+Yams.swift index 52e4f2e..8043614 100644 --- a/Sources/Yams/String+Yams.swift +++ b/Sources/Yams/String+Yams.swift @@ -13,8 +13,9 @@ extension String { /// line number, column and contents at utf8 offset. /// - /// - Parameter offset: Int - /// - Returns: lineNumber: line number start from 0, + /// - parameter offset: Int + /// + /// - returns: lineNumber: line number start from 0, /// column: utf16 column start from 0, /// contents: substring of line func utf8LineNumberColumnAndContents(at offset: Int) -> LineNumberColumnAndContents? { @@ -26,8 +27,9 @@ extension String { /// line number, column and contents at utf16 offset. /// - /// - Parameter offset: Int - /// - Returns: lineNumber: line number start from 0, + /// - parameter offset: Int + /// + /// - returns: lineNumber: line number start from 0, /// column: utf16 column start from 0, /// contents: substring of line func utf16LineNumberColumnAndContents(at offset: Int) -> LineNumberColumnAndContents? { @@ -39,8 +41,9 @@ extension String { /// line number, column and contents at Index. /// - /// - Parameter index: String.Index - /// - Returns: lineNumber: line number start from 0, + /// - 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 { @@ -66,8 +69,9 @@ extension String { /// substring indicated by line number. /// - /// - Parameter line: line number starts from 0. - /// - Returns: substring of line contains line ending characters + /// - 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 diff --git a/Sources/Yams/YamlError.swift b/Sources/Yams/YamlError.swift index 1a7d880..4a3a059 100644 --- a/Sources/Yams/YamlError.swift +++ b/Sources/Yams/YamlError.swift @@ -16,63 +16,65 @@ public enum YamlError: Swift.Error { // Used in `yaml_emitter_t` and `yaml_parser_t` /// `YAML_NO_ERROR`. No error is produced. case no - // swiftlint:disable:previous identifier_name /// `YAML_MEMORY_ERROR`. Cannot allocate or reallocate a block of memory. case memory // Used in `yaml_parser_t` /// `YAML_READER_ERROR`. Cannot read or decode the input stream. - /// - Parameters: - /// - problem: Error description. - /// - byteOffset: The byte about which the problem occured. - /// - value: The problematic value (-1 is none). - /// - yaml: YAML String which the problem occured while reading. + /// + /// - parameter problem: Error description. + /// - parameter byteOffset: The byte about which the problem occured. + /// - parameter value: The problematic value (-1 is none). + /// - parameter yaml: YAML String which the problem occured while reading. case reader(problem: String, byteOffset: Int, value: Int32, yaml: String) // line and column start from 1, column is counted by unicodeScalars /// `YAML_SCANNER_ERROR`. Cannot scan the input stream. - /// - Parameters: - /// - context: Error context. - /// - problem: Error description. - /// - mark: Problem position. - /// - yaml: YAML String which the problem occured while scanning. + /// + /// - parameter context: Error context. + /// - parameter problem: Error description. + /// - parameter mark: Problem position. + /// - parameter yaml: YAML String which the problem occured while scanning. case scanner(context: Context?, problem: String, Mark, yaml: String) /// `YAML_PARSER_ERROR`. Cannot parse the input stream. - /// - Parameters: - /// - context: Error context. - /// - problem: Error description. - /// - mark: Problem position. - /// - yaml: YAML String which the problem occured while parsing. + /// + /// - parameter context: Error context. + /// - parameter problem: Error description. + /// - parameter mark: Problem position. + /// - parameter yaml: YAML String which the problem occured while parsing. case parser(context: Context?, problem: String, Mark, yaml: String) /// `YAML_COMPOSER_ERROR`. Cannot compose a YAML document. - /// - Parameters: - /// - context: Error context. - /// - problem: Error description. - /// - mark: Problem position. - /// - yaml: YAML String which the problem occured while composing. + /// + /// - parameter context: Error context. + /// - parameter problem: Error description. + /// - parameter mark: Problem position. + /// - parameter yaml: YAML String which the problem occured while composing. case composer(context: Context?, problem: String, Mark, yaml: String) // Used in `yaml_emitter_t` /// `YAML_WRITER_ERROR`. Cannot write to the output stream. - /// - Parameter problem: Error description. + /// + /// - parameter problem: Error description. case writer(problem: String) /// `YAML_EMITTER_ERROR`. Cannot emit a YAML stream. - /// - Parameter problem: Error description. + /// + /// - parameter problem: Error description. case emitter(problem: String) - /// Used in `NodeRepresentable` - /// - Parameter problem: Error description. + /// Used in `NodeRepresentable`. + /// + /// - parameter problem: Error description. case representer(problem: String) - /// The error context + /// The error context. public struct Context: CustomStringConvertible { - /// context text + /// Context text. public let text: String - /// context position + /// Context position. public let mark: Mark /// A textual representation of this instance. public var description: String { diff --git a/Tests/YamsTests/TestHelper.swift b/Tests/YamsTests/TestHelper.swift index 9e40b5b..91551e6 100644 --- a/Tests/YamsTests/TestHelper.swift +++ b/Tests/YamsTests/TestHelper.swift @@ -35,16 +35,16 @@ func timestamp(_ timeZoneHour: Int = 0, /// AssertEqual for Any /// -/// - Parameters: -/// - lhs: Any -/// - rhs: Any -/// - context: Closure generating String that used on generating assertion -/// - file: file path string -/// - line: line number -/// - Returns: true if lhs is equal to rhs +/// - parameter lhs: Any +/// - parameter rhs: Any +/// - parameter context: Closure generating String that used on generating assertion +/// - parameter file: file path string +/// - parameter line: line number +/// +/// - returns: true if lhs is equal to rhs @discardableResult func YamsAssertEqual(_ lhs: Any?, _ rhs: Any?, - // swiftlint:disable:previous function_body_length identifier_name + // swiftlint:disable:previous function_body_length _ context: @autoclosure @escaping () -> String = "", file: StaticString = #file, line: UInt = #line) -> Bool { // use inner function for capturing `file` and `line`