diff --git a/Packages/Node-1.0.1/.gitignore b/Packages/Node-1.0.1/.gitignore new file mode 100644 index 0000000..3850f01 --- /dev/null +++ b/Packages/Node-1.0.1/.gitignore @@ -0,0 +1,39 @@ +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +*.xcsmblueprint + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control +# +Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +Carthage/Checkouts + +Carthage/Build + +# Swift Package Manager +.build/ +Packages/ +*.xcodeproj diff --git a/Packages/Node-1.0.1/.travis.yml b/Packages/Node-1.0.1/.travis.yml new file mode 100644 index 0000000..6f74cb2 --- /dev/null +++ b/Packages/Node-1.0.1/.travis.yml @@ -0,0 +1,10 @@ +os: + - linux + - osx +language: generic +sudo: required +dist: trusty +osx_image: xcode8 +script: + - eval "$(curl -sL swift.vapor.sh/ci)" + - eval "$(curl -sL swift.vapor.sh/codecov)" diff --git a/Packages/Node-1.0.1/LICENSE b/Packages/Node-1.0.1/LICENSE new file mode 100644 index 0000000..242132d --- /dev/null +++ b/Packages/Node-1.0.1/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Packages/Node-1.0.1/Package.swift b/Packages/Node-1.0.1/Package.swift new file mode 100644 index 0000000..ab7747b --- /dev/null +++ b/Packages/Node-1.0.1/Package.swift @@ -0,0 +1,14 @@ +import PackageDescription + +let package = Package( + name: "Node", + targets: [ + Target( + name: "Node" + ), + ], + dependencies: [ + .Package(url: "https://github.com/vapor/path-indexable.git", majorVersion: 1), + .Package(url: "https://github.com/vapor/polymorphic.git", majorVersion: 1) + ] +) diff --git a/Packages/Node-1.0.1/README.md b/Packages/Node-1.0.1/README.md new file mode 100644 index 0000000..18217bb --- /dev/null +++ b/Packages/Node-1.0.1/README.md @@ -0,0 +1,24 @@ +# Node + +![Swift](http://img.shields.io/badge/swift-3.0-brightgreen.svg) +[![Build Status](https://travis-ci.org/vapor/node.svg?branch=master)](https://travis-ci.org/vapor/node) +[![CircleCI](https://circleci.com/gh/vapor/node.svg?style=shield)](https://circleci.com/gh/vapor/node) +[![Code Coverage](https://codecov.io/gh/vapor/node/branch/master/graph/badge.svg)](https://codecov.io/gh/vapor/node) +[![Codebeat](https://codebeat.co/badges/a793ad97-47e3-40d9-82cf-2aafc516ef4e)](https://codebeat.co/projects/github-com-vapor-node) +[![Slack Status](http://vapor.team/badge.svg)](http://vapor.team) + +The purpose of this package is to be an intermediary data layer that can allow transformation between unrelated formats. In this way any node convertible object can be converted to any other node convertible object and vice versa. + +> a point at which lines or pathways intersect or branch; a central or connecting point. + +## 📖 Documentation + +Visit the Vapor web framework's [documentation](http://docs.vapor.codes) for instructions on how to use this package. + +## 💧 Community + +Join the welcoming community of fellow Vapor developers in [slack](http://vapor.team). + +## 🔧 Compatibility + +This library has been tested on macOS and Ubuntu. diff --git a/Packages/Node-1.0.1/Sources/Node/Convertible/Bool+Convertible.swift b/Packages/Node-1.0.1/Sources/Node/Convertible/Bool+Convertible.swift new file mode 100644 index 0000000..3b4591e --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Convertible/Bool+Convertible.swift @@ -0,0 +1,12 @@ +extension Bool: NodeConvertible { + public func makeNode(context: Context = EmptyNode) -> Node { + return .bool(self) + } + + public init(node: Node, in context: Context) throws { + guard let bool = node.bool else { + throw NodeError.unableToConvert(node: node, expected: "\(Bool.self)") + } + self = bool + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Convertible/Context.swift b/Packages/Node-1.0.1/Sources/Node/Convertible/Context.swift new file mode 100644 index 0000000..70df1a6 --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Convertible/Context.swift @@ -0,0 +1,13 @@ +/** + Sometimes convertible operations require a greater context beyond + just a Node. + + Any object can conform to Context and be included in initialization +*/ +public protocol Context {} + +extension Node : Context {} +extension Array : Context {} +extension Dictionary : Context {} + +public let EmptyNode = Node.object([:]) diff --git a/Packages/Node-1.0.1/Sources/Node/Convertible/Convertible+Init.swift b/Packages/Node-1.0.1/Sources/Node/Convertible/Convertible+Init.swift new file mode 100644 index 0000000..5fc8cb9 --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Convertible/Convertible+Init.swift @@ -0,0 +1,75 @@ +extension NodeRepresentable { + /** + Map the node back to a convertible type + + - parameter type: the type to map to -- can be inferred + - throws: if mapping fails + - returns: convertible representation of object + */ + public func converted( + to type: T.Type = T.self, + in context: Context = EmptyNode) throws -> T { + let node = try makeNode() + return try type.init(node: node, in: context) + } +} + +extension NodeInitializable { + public init(node representable: NodeRepresentable, in context: Context = EmptyNode) throws { + let node = try representable.makeNode() + try self.init(node: node, in: context) + } + + public init(node representable: NodeRepresentable?, in context: Context = EmptyNode) throws { + let node = try representable?.makeNode() ?? .null + try self.init(node: node, in: context) + } +} + +// MARK: Non-Homogenous + +extension NodeInitializable { + public init(node representable: [String: NodeRepresentable], in context: Context = EmptyNode) throws { + var converted: [String: Node] = [:] + + for (key, val) in representable { + converted[key] = try Node(node: val) + } + + let node = Node.object(converted) + try self.init(node: node, in: context) + } + + public init(node representable: [String: NodeRepresentable?], in context: Context = EmptyNode) throws { + var converted: [String: Node] = [:] + + for (key, val) in representable { + converted[key] = try Node(node: val) + } + + let node = Node.object(converted) + try self.init(node: node, in: context) + } + + public init(node representable: [NodeRepresentable], in context: Context = EmptyNode) throws { + var converted: [Node] = [] + + for val in representable { + converted.append(try Node(node: val)) + } + + let node = Node.array(converted) + try self.init(node: node, in: context) + } + + public init(node representable: [NodeRepresentable?], in context: Context = EmptyNode) throws { + var converted: [Node] = [] + + for val in representable { + converted.append(try Node(node: val)) + } + + let node = Node.array(converted) + try self.init(node: node, in: context) + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Convertible/Convertible.swift b/Packages/Node-1.0.1/Sources/Node/Convertible/Convertible.swift new file mode 100644 index 0000000..0b609f9 --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Convertible/Convertible.swift @@ -0,0 +1,44 @@ +public protocol NodeRepresentable { + /** + Turn the convertible into a node + + - throws: if convertible can not create a Node + - returns: a node if possible + */ + func makeNode(context: Context) throws -> Node +} + +extension NodeRepresentable { + public func makeNode() throws -> Node { + return try makeNode(context: EmptyNode) + } +} + +public protocol NodeInitializable { + /** + Initialize the convertible with a node within a context. + + Context is an empty protocol to which any type can conform. + This allows flexibility. for objects that might require access + to a context outside of the node ecosystem + */ + init(node: Node, in context: Context) throws +} + +extension NodeInitializable { + /** + Default initializer for cases where a custom Context is not required + */ + public init(node: Node) throws { + try self.init(node: node, in: EmptyNode) + } +} + +/** + The underlying protocol used for all conversions. + This is the base of all conversions, where both sides of data are NodeConvertible. + Any NodeConvertible can be turned into any other NodeConvertible type + + Json => Node => Object => Node => XML => ... +*/ +public protocol NodeConvertible: NodeInitializable, NodeRepresentable {} diff --git a/Packages/Node-1.0.1/Sources/Node/Convertible/FloatingPoint+Convertible.swift b/Packages/Node-1.0.1/Sources/Node/Convertible/FloatingPoint+Convertible.swift new file mode 100644 index 0000000..d482f3c --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Convertible/FloatingPoint+Convertible.swift @@ -0,0 +1,29 @@ +public protocol NodeConvertibleFloatingPointType: NodeConvertible { + var doubleValue: Double { get } + init(_ other: Double) +} + +extension Float: NodeConvertibleFloatingPointType { + public var doubleValue: Double { + return Double(self) + } +} + +extension Double: NodeConvertibleFloatingPointType { + public var doubleValue: Double { + return Double(self) + } +} + +extension NodeConvertibleFloatingPointType { + public func makeNode(context: Context = EmptyNode) -> Node { + return .number(Node.Number(doubleValue)) + } + + public init(node: Node, in context: Context) throws { + guard let double = node.double else { + throw NodeError.unableToConvert(node: node, expected: "\(Self.self)") + } + self.init(double) + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Convertible/Integer+Convertible.swift b/Packages/Node-1.0.1/Sources/Node/Convertible/Integer+Convertible.swift new file mode 100644 index 0000000..05dc24e --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Convertible/Integer+Convertible.swift @@ -0,0 +1,20 @@ +extension Int: NodeConvertible {} +extension Int8: NodeConvertible {} +extension Int16: NodeConvertible {} +extension Int32: NodeConvertible {} +extension Int64: NodeConvertible {} + +extension SignedInteger { + public func makeNode(context: Context = EmptyNode) -> Node { + let number = Node.Number(self.toIntMax()) + return .number(number) + } + + public init(node: Node, in context: Context) throws { + guard let int = node.int else { + throw NodeError.unableToConvert(node: node, expected: "\(Self.self)") + } + + self.init(int.toIntMax()) + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Convertible/Node+Convertible.swift b/Packages/Node-1.0.1/Sources/Node/Convertible/Node+Convertible.swift new file mode 100644 index 0000000..f3dd561 --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Convertible/Node+Convertible.swift @@ -0,0 +1,9 @@ +extension Node: NodeConvertible { // Can conform to both if non-throwing implementations + public init(node: Node, in context: Context) { + self = node + } + + public func makeNode(context: Context = EmptyNode) -> Node { + return self + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Convertible/Sequence+Convertible.swift b/Packages/Node-1.0.1/Sources/Node/Convertible/Sequence+Convertible.swift new file mode 100644 index 0000000..18b8def --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Convertible/Sequence+Convertible.swift @@ -0,0 +1,119 @@ +public protocol KeyAccessible { + associatedtype Key: Hashable + associatedtype Value + var allItems: [(Key, Value)] { get } + subscript(key: Key) -> Value? { get set } + init(dictionary: [Key: Value]) +} + +extension Dictionary: KeyAccessible { + public var allItems: [(Key, Value)] { return Array(self) } + public init(dictionary: [Key: Value]) { + self = dictionary + } +} + +// MARK: Arrays + +extension Sequence where Iterator.Element: NodeRepresentable { + public func makeNode(context: Context = EmptyNode) throws -> Node { + let array = try map { try $0.makeNode(context: context) } + return Node(array) + } + + public func converted(to type: [T].Type = [T].self) throws -> [T] { + return try map { try $0.converted() } + } +} + +extension Sequence where Iterator.Element == NodeRepresentable { + public func makeNode(context: Context = EmptyNode) throws -> Node { + let array = try map { try $0.makeNode(context: context) } + return Node(array) + } + + public func converted(to type: [T].Type = [T].self) throws -> [T] { + return try map { try $0.converted() } + } +} + +extension KeyAccessible where Key == String, Value: NodeRepresentable { + public func makeNode(context: Context = EmptyNode) throws -> Node { + var mutable: [String : Node] = [:] + try allItems.forEach { key, value in + mutable[key] = try value.makeNode() + } + return .object(mutable) + } + + public func converted(to type: T.Type = T.self) throws -> T { + return try makeNode().converted() + } +} + +extension KeyAccessible where Key == String, Value == NodeRepresentable { + public func makeNode(context: Context = EmptyNode) throws -> Node { + var mutable: [String : Node] = [:] + try allItems.forEach { key, value in + mutable[key] = try value.makeNode() + } + return .object(mutable) + } + + public func converted(to type: T.Type = T.self) throws -> T { + return try makeNode().converted() + } +} + +// MARK: From Node + +extension Array where Element: NodeInitializable { + public init(node: NodeRepresentable, in context: Context = EmptyNode) throws { + let node = try node.makeNode(context: context) + let array = node.nodeArray ?? [node] + self = try array + .map { try Element(node: $0, in: context) } + } + +} + +extension Set where Element: NodeInitializable { + public init(node: NodeRepresentable, in context: Context = EmptyNode) throws { + let node = try node.makeNode(context: context) + let array = try [Element](node: node, in: context) + self = Set(array) + } +} + +extension KeyAccessible where Key == String, Value: NodeInitializable { + public init(node: NodeRepresentable, in context: Context = EmptyNode) throws { + let node = try node.makeNode(context: context) + guard let object = node.nodeObject else { + throw NodeError.unableToConvert(node: node, expected: "\([Key: Value].self)") + } + + var mapped: [String: Value] = [:] + try object.forEach { key, value in + mapped[key] = try Value(node: value, in: context) + } + self.init(dictionary: mapped) + } +} + +// MARK: Mappings + +extension Sequence where Iterator.Element: NodeRepresentable { + public func map(to type: N.Type, in context: Context = EmptyNode) throws -> [N] { + return try map { try N(node: $0, in: context) } + } +} + +extension Sequence where Iterator.Element == NodeRepresentable { + public func map(to type: N.Type, in context: Context = EmptyNode) throws -> [N] { + return try map { try N(node: $0, in: context) } + } +} + +extension Sequence where Iterator.Element: Hashable { + public var set: Set { return Set(self) } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Convertible/String+Convertible.swift b/Packages/Node-1.0.1/Sources/Node/Convertible/String+Convertible.swift new file mode 100644 index 0000000..c33536b --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Convertible/String+Convertible.swift @@ -0,0 +1,12 @@ +extension String: NodeConvertible { + public func makeNode(context: Context = EmptyNode) -> Node { + return .string(self) + } + + public init(node: Node, in context: Context) throws { + guard let string = node.string else { + throw NodeError.unableToConvert(node: node, expected: "\(String.self)") + } + self = string + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Convertible/UnsignedInteger+Convertible.swift b/Packages/Node-1.0.1/Sources/Node/Convertible/UnsignedInteger+Convertible.swift new file mode 100644 index 0000000..f6239a3 --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Convertible/UnsignedInteger+Convertible.swift @@ -0,0 +1,20 @@ +extension UInt: NodeConvertible {} +extension UInt8: NodeConvertible {} +extension UInt16: NodeConvertible {} +extension UInt32: NodeConvertible {} +extension UInt64: NodeConvertible {} + +extension UnsignedInteger { + public func makeNode(context: Context = EmptyNode) -> Node { + let number = Node.Number(self.toUIntMax()) + return .number(number) + } + + public init(node: Node, in context: Context) throws { + guard let int = node.uint else { + throw NodeError.unableToConvert(node: node, expected: "\(Self.self)") + } + + self.init(int.toUIntMax()) + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Core/Node+Accessors.swift b/Packages/Node-1.0.1/Sources/Node/Core/Node+Accessors.swift new file mode 100644 index 0000000..2be5de3 --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Core/Node+Accessors.swift @@ -0,0 +1,19 @@ +extension Node { + public var nodeArray: [Node]? { + switch self { + case let .array(array): + return array + default: + return nil + } + } + + public var nodeObject: [String: Node]? { + switch self { + case let .object(ob): + return ob + default: + return nil + } + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Core/Node+Equatable.swift b/Packages/Node-1.0.1/Sources/Node/Core/Node+Equatable.swift new file mode 100644 index 0000000..93731ab --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Core/Node+Equatable.swift @@ -0,0 +1,22 @@ +extension Node: Equatable {} + +public func ==(lhs: Node, rhs: Node) -> Bool { + switch (lhs, rhs) { + case (.null, .null): + return true + case let (.bool(l), .bool(r)): + return l == r + case let (.number(l), .number(r)): + return l == r + case let (.string(l), .string(r)): + return l == r + case let (.array(l), .array(r)): + return l == r + case let (.object(l), .object(r)): + return l == r + case let (.bytes(l), .bytes(r)): + return l == r + default: + return false + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Core/Node+Exports.swift b/Packages/Node-1.0.1/Sources/Node/Core/Node+Exports.swift new file mode 100644 index 0000000..2585bbe --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Core/Node+Exports.swift @@ -0,0 +1,3 @@ +// These are critical components to Node and are being exported +@_exported import Polymorphic +@_exported import PathIndexable diff --git a/Packages/Node-1.0.1/Sources/Node/Core/Node+Init.swift b/Packages/Node-1.0.1/Sources/Node/Core/Node+Init.swift new file mode 100644 index 0000000..a7d7e88 --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Core/Node+Init.swift @@ -0,0 +1,38 @@ +extension Node { + public init(_ value: Bool) { + self = .bool(value) + } + + public init(_ value: String) { + self = .string(value) + } + + public init(_ int: Int) { + self = .number(Number(int)) + } + + public init(_ double: Double) { + self = .number(Number(double)) + } + + public init(_ uint: UInt) { + self = .number(Number(uint)) + } + + public init(_ number: Number) { + self = .number(number) + } + + public init(_ value: [Node]) { + let array = [Node](value) + self = .array(array) + } + + public init(_ value: [String : Node]) { + self = .object(value) + } + + public init(bytes: [UInt8]) { + self = .bytes(bytes) + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Core/Node+Literals.swift b/Packages/Node-1.0.1/Sources/Node/Core/Node+Literals.swift new file mode 100644 index 0000000..c9b9e19 --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Core/Node+Literals.swift @@ -0,0 +1,53 @@ +extension Node: ExpressibleByNilLiteral { + public init(nilLiteral value: Void) { + self = .null + } +} + +extension Node: ExpressibleByBooleanLiteral { + public init(booleanLiteral value: BooleanLiteralType) { + self.init(value) + } +} + +extension Node: ExpressibleByIntegerLiteral { + public init(integerLiteral value: IntegerLiteralType) { + self = value.makeNode(context: EmptyNode) + } +} + +extension Node: ExpressibleByFloatLiteral { + public init(floatLiteral value: FloatLiteralType) { + self = value.makeNode(context: EmptyNode) + } +} + +extension Node: ExpressibleByStringLiteral { + public init(unicodeScalarLiteral value: String) { + self.init(value) + } + + public init(extendedGraphemeClusterLiteral value: String) { + self.init(value) + } + + public init(stringLiteral value: String) { + self.init(value) + } +} + +extension Node: ExpressibleByArrayLiteral { + public init(arrayLiteral elements: Node...) { + self = .array(elements) + } +} + +extension Node: ExpressibleByDictionaryLiteral { + public init(dictionaryLiteral elements: (String, Node)...) { + var object = [String : Node](minimumCapacity: elements.count) + elements.forEach { key, value in + object[key] = value + } + self = .object(object) + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Core/Node+PathIndexable.swift b/Packages/Node-1.0.1/Sources/Node/Core/Node+PathIndexable.swift new file mode 100644 index 0000000..acb2d5f --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Core/Node+PathIndexable.swift @@ -0,0 +1,11 @@ +extension Node: PathIndexable { + /// If self is an array representation, return array + public var pathIndexableArray: [Node]? { + return nodeArray + } + + /// If self is an object representation, return object + public var pathIndexableObject: [String: Node]? { + return nodeObject + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Core/Node+Polymorphic.swift b/Packages/Node-1.0.1/Sources/Node/Core/Node+Polymorphic.swift new file mode 100644 index 0000000..b7e200b --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Core/Node+Polymorphic.swift @@ -0,0 +1,114 @@ +extension Node: Polymorphic { + public var string: String? { + switch self { + case .bool(let bool): + return "\(bool)" + case .number(let number): + return "\(number)" + case .string(let string): + return string + default: + return nil + } + } + + public var int: Int? { + switch self { + case .string(let string): + return string.int + case .number(let number): + return number.int + case .bool(let bool): + return bool ? 1 : 0 + default: + return nil + } + } + + public var uint: UInt? { + switch self { + case .string(let string): + return string.uint + case .number(let number): + return number.uint + case .bool(let bool): + return bool ? 1 : 0 + default: + return nil + } + } + + public var double: Double? { + switch self { + case .number(let number): + return number.double + case .string(let string): + return string.double + case .bool(let bool): + return bool ? 1.0 : 0.0 + default: + return nil + } + } + + public var isNull: Bool { + switch self { + case .null: + return true + case .string(let string): + return string.isNull + default: + return false + } + } + + public var bool: Bool? { + switch self { + case .bool(let bool): + return bool + case .number(let number): + return number.bool + case .string(let string): + return string.bool + case .null: + return false + default: + return nil + } + } + + public var float: Float? { + switch self { + case .number(let number): + return Float(number.double) + case .string(let string): + return string.float + case .bool(let bool): + return bool ? 1.0 : 0.0 + default: + return nil + } + } + + public var array: [Polymorphic]? { + switch self { + case .array(let array): + return array.map { $0 } + case .string(let string): + return string.array + default: + return nil + } + } + + public var object: [String: Polymorphic]? { + guard case let .object(ob) = self else { return nil } + var object: [String: Polymorphic] = [:] + + ob.forEach { key, value in + object[key] = value + } + + return object + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Core/Node.swift b/Packages/Node-1.0.1/Sources/Node/Core/Node.swift new file mode 100644 index 0000000..11daffb --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Core/Node.swift @@ -0,0 +1,13 @@ +/** + Node is meant to be a transitive data structure that can be used to facilitate conversions + between different types. +*/ +public enum Node { + case null + case bool(Bool) + case number(Number) + case string(String) + case array([Node]) + case object([String: Node]) + case bytes([UInt8]) +} diff --git a/Packages/Node-1.0.1/Sources/Node/Extract/Node+Extract.swift b/Packages/Node-1.0.1/Sources/Node/Extract/Node+Extract.swift new file mode 100644 index 0000000..4b13aac --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Extract/Node+Extract.swift @@ -0,0 +1,251 @@ + +extension Dictionary { + func mapValues(_ mapper: (_ value: Value) throws -> T) + rethrows -> Dictionary { + var mapped: [Key: T] = [:] + try forEach { key, value in + mapped[key] = try mapper(value) + } + return mapped + } +} + +extension Node: NodeBacked { + public var node: Node { + get { + return self + } + set { + self = newValue + } + } + + public init(_ node: Node) { + self = node + } +} + +// MARK: Transforming + +extension NodeBacked { + public func extract( + _ path: PathIndex..., + transform: (InputType) throws -> T) + throws -> T { + return try extract(path: path, transform: transform) + } + + public func extract( + path: [PathIndex], + transform: (InputType) throws -> T) + throws -> T { + guard let value = node[path] else { + throw NodeError.unableToConvert(node: nil, expected: "\(T.self)") + } + + let input = try InputType(node: value) + return try transform(input) + } + + public func extract( + _ path: PathIndex..., + transform: (InputType?) throws -> T) + throws -> T { + return try extract(path: path, transform: transform) + } + + public func extract( + path: [PathIndex], + transform: (InputType?) throws -> T) + throws -> T { + return try transform(extract(path)) + } +} + +// MARK: Non-Optional + +extension NodeBacked { + public func extract( + _ path: PathIndex...) + throws -> T { + return try extract(path) + } + + public func extract( + _ path: [PathIndex]) + throws -> T { + guard let value = node[path] else { + throw NodeError.unableToConvert(node: nil, expected: "\(T.self)") + } + return try T(node: value) + } + + public func extract( + _ path: PathIndex...) + throws -> [T] { + return try extract(path) + } + + public func extract( + _ path: [PathIndex]) + throws -> [T] { + guard let value = node[path] else { + throw NodeError.unableToConvert(node: nil, expected: "\([T].self)") + } + return try [T](node: value) + } + + public func extract( + _ path: PathIndex...) + throws -> [[T]] { + return try extract(path) + } + + public func extract( + _ path: [PathIndex]) + throws -> [[T]] { + guard let initial = node[path] else { + throw NodeError.unableToConvert(node: nil, expected: "\([[T]].self)") + } + let array = initial.nodeArray ?? [initial] + return try array.map { try [T](node: $0) } + } + + public func extract( + _ path: PathIndex...) + throws -> [String : T] { + return try extract(path) + } + + public func extract( + _ path: [PathIndex]) + throws -> [String : T] { + let value = node[path] + guard let object = value?.nodeObject else { + throw NodeError.unableToConvert(node: value, expected: "\([String: T].self)") + } + return try object.mapValues { return try T(node: $0) } + } + + public func extract( + _ path: PathIndex...) + throws -> [String : [T]] { + return try extract(path) + } + + public func extract( + _ path: [PathIndex]) + throws -> [String : [T]] { + let value = node[path] + guard let object = value?.nodeObject else { + throw NodeError.unableToConvert(node: value, expected: "\([String: [T]].self)") + } + return try object.mapValues { return try [T](node: $0) } + } + + public func extract( + _ path: PathIndex...) + throws -> Set { + return try extract(path) + } + + public func extract( + _ path: [PathIndex]) + throws -> Set { + guard let value = node[path] else { + throw NodeError.unableToConvert(node: nil, expected: "\(Set.self)") + } + let array = try [T](node: value) + return Set(array) + } +} + +// MARK: Optional Extractions + +extension NodeBacked { + public func extract( + _ path: PathIndex...) + throws -> T? { + return try extract(path) + } + + public func extract( + _ path: [PathIndex]) + throws -> T? { + guard let node = node[path], node != .null else { return nil } + return try T(node: node) + } + + public func extract( + _ path: PathIndex...) + throws -> [T]? { + return try extract(path) + } + + public func extract( + _ path: [PathIndex]) + throws -> [T]? { + guard let node = node[path], node != .null else { return nil } + return try [T](node: node) + } + + public func extract( + _ path: PathIndex...) + throws -> [[T]]? { + return try extract(path) + } + + public func extract( + _ path: [PathIndex]) + throws -> [[T]]? { + guard let node = node[path], node != .null else { return nil } + let array = node.nodeArray ?? [node] + return try array.map { try [T](node: $0) } + } + + public func extract( + _ path: PathIndex...) + throws -> [String : T]? { + return try extract(path) + } + + public func extract( + _ path: [PathIndex]) + throws -> [String : T]? { + guard let node = node[path], node != .null else { return nil } + guard let object = node.nodeObject else { + throw NodeError.unableToConvert(node: node, expected: "\([String: T].self)") + } + return try object.mapValues { return try T(node: $0) } + } + + public func extract( + _ path: PathIndex...) + throws -> [String : [T]]? { + return try extract(path) + } + + public func extract( + _ path: [PathIndex]) + throws -> [String : [T]]? { + guard let node = node[path], node != .null else { return nil } + guard let object = node.nodeObject else { + throw NodeError.unableToConvert(node: node, expected: "\([String: [T]].self)") + } + return try object.mapValues { return try [T](node: $0) } + } + + public func extract( + _ path: PathIndex...) + throws -> Set? { + return try extract(path) + } + + public func extract( + _ path: [PathIndex]) + throws -> Set? { + guard let node = node[path], node != .null else { return nil } + let array = try [T](node: node) + return Set(array) + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/NodeBacked.swift b/Packages/Node-1.0.1/Sources/Node/NodeBacked.swift new file mode 100644 index 0000000..164d835 --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/NodeBacked.swift @@ -0,0 +1,80 @@ +public protocol NodeBacked: NodeConvertible, PathIndexable, Polymorphic { + var node: Node { get set } + init(_ node: Node) +} + +// Convertible +extension NodeBacked { + public init(node: Node, in context: Context) throws { + self.init(node) + } + + public func makeNode(context: Context = EmptyNode) -> Node { + return node + } +} + +// Polymorphic +extension NodeBacked { + public var isNull: Bool { return node.isNull } + public var bool: Bool? { return node.bool } + public var double: Double? { return node.double } + public var int: Int? { return node.int } + public var string: String? { return node.string } + public var array: [Polymorphic]? { + return node.nodeArray?.map { Self($0) } + } + public var object: [String: Polymorphic]? { + return node.nodeObject.flatMap { ob in + var result = [String: Polymorphic]() + ob.forEach { k, v in + result[k] = Self(v) + } + return result + } + } +} + +// PathIndexable +extension NodeBacked { + + /** + If self is an array representation, return array + */ + public var pathIndexableArray: [Self]? { + return node.nodeArray?.map { Self($0) } + } + + /** + If self is an object representation, return object + */ + public var pathIndexableObject: [String: Self]? { + guard let o = node.nodeObject else { return nil } + var object: [String: Self] = [:] + for (key, val) in o { + object[key] = Self(val) + } + return object + } + + /** + Initialize json w/ array + */ + public init(_ array: [Self]) { + let array = array.map { $0.node } + let node = Node.array(array) + self.init(node) + } + + /** + Initialize json w/ object + */ + public init(_ o: [String: Self]) { + var object: [String: Node] = [:] + for (key, val) in o { + object[key] = val.node + } + let node = Node.object(object) + self.init(node) + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Number/Number.swift b/Packages/Node-1.0.1/Sources/Node/Number/Number.swift new file mode 100644 index 0000000..fc61805 --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Number/Number.swift @@ -0,0 +1,165 @@ +extension Node { + public enum Number { + case int(Int) + case uint(UInt) + case double(Double) + } +} + +// MARK: Initializers + +extension Node.Number { + public init(_ value: I) { + let max = value.toIntMax() + let int = Int(max) + self = .int(int) + } + + public init(_ value: U) { + let max = value.toUIntMax() + let uint = UInt(max) + self = .uint(uint) + } + + public init(_ value: Float) { + let double = Double(value) + self = .init(double) + } + + public init(_ value: Double) { + self = .double(value) + } +} + +// MARK: Accessors + +extension UInt { + static var intMax = UInt(Int.max) +} + +extension Node.Number { + public var int: Int { + switch self { + case let .int(i): + return i + case let .uint(u): + guard u < UInt.intMax else { return Int.max } + return Int(u) + case let .double(d): + return Int(d) + } + } + + public var uint: UInt { + switch self { + case let .int(i): + guard i > 0 else { return 0 } + return UInt(i) + case let .uint(u): + return u + case let .double(d): + return UInt(d) + } + } + + public var double: Double { + switch self { + case let .int(i): + return Double(i) + case let .uint(u): + return Double(u) + case let .double(d): + return Double(d) + } + } +} + +extension Node.Number { + public var bool: Bool? { + switch self { + case let .int(i): + switch i { + case 1: return true + case 0: return false + default: + return nil + } + case let .uint(u): + switch u { + case 1: return true + case 0: return false + default: + return nil + } + case let .double(d): + switch d { + case 1.0: return true + case 0.0: return false + default: + return nil + } + } + } +} + +// MARK: Equatable + +extension Node.Number: Equatable {} + +public func ==(lhs: Node.Number, rhs: Node.Number) -> Bool { + switch (lhs, rhs) { + case let (.int(l), .int(r)): + return l == r + case let (.int(l), .uint(r)): + guard l >= 0 && r <= UInt(Int.max) else { return false } + return l == Int(r) + case let (.int(l), .double(r)): + guard r.truncatingRemainder(dividingBy: 1) == 0.0 else { return false } + return l == Int(r) + case let (.uint(l), .int(r)): + guard l <= UInt(Int.max) && r >= 0 else { return false } + return Int(l) == r + case let (.uint(l), .uint(r)): + return l == r + case let (.uint(l), .double(r)): + guard r >= 0 && r.truncatingRemainder(dividingBy: 1) == 0.0 else { return false } + return l == UInt(r) + case let (.double(l), .int(r)): + guard l.truncatingRemainder(dividingBy: 1) == 0.0 else { return false } + return Int(l) == r + case let (.double(l), .uint(r)): + guard l.truncatingRemainder(dividingBy: 1) == 0.0 else { return false } + return UInt(l) == r + case let (.double(l), .double(r)): + return l == r + } +} + +// MARK: Literals + +extension Node.Number: ExpressibleByIntegerLiteral { + public init(integerLiteral value: IntegerLiteralType) { + self.init(value) + } +} + +extension Node.Number: ExpressibleByFloatLiteral { + public init(floatLiteral value: FloatLiteralType) { + self.init(value) + } +} + +// MARK: String + +extension Node.Number: CustomStringConvertible { + public var description: String { + switch self { + case let .int(i): + return i.description + case let .uint(u): + return u.description + case let .double(d): + return d.description + } + } +} diff --git a/Packages/Node-1.0.1/Sources/Node/Utilities/Errors.swift b/Packages/Node-1.0.1/Sources/Node/Utilities/Errors.swift new file mode 100644 index 0000000..c268a77 --- /dev/null +++ b/Packages/Node-1.0.1/Sources/Node/Utilities/Errors.swift @@ -0,0 +1,9 @@ +public enum NodeError: Swift.Error { + /** + Unable to convert a given node to the target type. + + - param node: the node that was unable to convert + - param expected: a description of the type Genome was trying to convert to + */ + case unableToConvert(node: Node?, expected: String) +} diff --git a/Packages/Node-1.0.1/Tests/Info.plist b/Packages/Node-1.0.1/Tests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/Packages/Node-1.0.1/Tests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Packages/Node-1.0.1/Tests/LinuxMain.swift b/Packages/Node-1.0.1/Tests/LinuxMain.swift new file mode 100644 index 0000000..b75ff82 --- /dev/null +++ b/Packages/Node-1.0.1/Tests/LinuxMain.swift @@ -0,0 +1,17 @@ +#if os(Linux) +import XCTest +@testable import NodeTests + +XCTMain([ + testCase(BasicConvertibleTests.allTests), + testCase(DictionaryKeyPathTests.allTests), + testCase(NodeDataTypeTests.allTests), + testCase(NodeExtractTests.allTests), + testCase(NodeIndexableTests.allTests), + testCase(NodePolymorphicTests.allTests), + testCase(NodeTests.allTests), + testCase(SequenceConvertibleTests.allTests), + testCase(NumberTests.allTests), + testCase(NodeBackedTests.allTests), +]) +#endif diff --git a/Packages/Node-1.0.1/Tests/NodeTests/BasicConvertibleTests.swift b/Packages/Node-1.0.1/Tests/NodeTests/BasicConvertibleTests.swift new file mode 100644 index 0000000..ff93319 --- /dev/null +++ b/Packages/Node-1.0.1/Tests/NodeTests/BasicConvertibleTests.swift @@ -0,0 +1,180 @@ +// +// ConvertibleTests.swift +// Node +// +// Created by Logan Wright on 7/20/16. +// +// + +import XCTest +import Node + +class BasicConvertibleTests: XCTestCase { + static let allTests = [ + ("testBoolInit", testBoolInit), + ("testBoolRepresent", testBoolRepresent), + ("testIntegerInit", testIntegerInit), + ("testIntegerRepresent", testIntegerRepresent), + ("testDoubleInit", testDoubleInit), + ("testDoubleRepresent", testDoubleRepresent), + + ("testFloatInit", testFloatInit), + ("testFloatRepresent", testFloatRepresent), + ("testUnsignedIntegerInit", testUnsignedIntegerInit), + ("testUnsignedIntegerRepresent", testUnsignedIntegerRepresent), + ("testStringInit", testStringInit), + ("testStringRepresent", testStringRepresent), + ("testNodeConvertible", testNodeConvertible), + ] + + func testBoolInit() throws { + let truths: [Node] = [ + "true", "t", "yes", "y", 1, 1.0, "1" + ] + try truths.forEach { truth in try XCTAssert(Bool(node: truth)) } + + let falsehoods: [Node] = [ + "false", "f", "no", "n", 0, 0.0, "0" + ] + try falsehoods.forEach { falsehood in try XCTAssert(!Bool(node: falsehood)) } + + let fails: [Node] = [ + [1,2,3], ["key": "value"], .null + ] + try assert(Bool.self, fails: fails) + } + + func testBoolRepresent() { + let truthy = true.makeNode() + let falsy = false.makeNode() + XCTAssert(truthy == .bool(true)) + XCTAssert(falsy == .bool(false)) + } + + func testIntegerInit() throws { + let string = Node("400") + let int = Node(-42) + let double = Node(55.6) + let bool = Node(true) + + try XCTAssert(Int(node: string) == 400) + try XCTAssert(Int(node: int) == -42) + try XCTAssert(Int(node: double) == 55) + try XCTAssert(Int(node: bool) == 1) + + let fails: [Node] = [ + [1,2,3], ["key": "value"], .null + ] + try assert(Int.self, fails: fails) + } + + func testIntegerRepresent() throws { + let node = try 124.makeNode() + XCTAssert(node == .number(124)) + } + + func testDoubleInit() throws { + let string = Node("433.1029") + let int = Node(-42) + let double = Node(55.6) + let bool = Node(true) + + try XCTAssert(Double(node: string) == 433.1029) + try XCTAssert(Double(node: int) == -42.0) + try XCTAssert(Double(node: double) == 55.6) + try XCTAssert(Double(node: bool) == 1.0) + + let fails: [Node] = [ + [1,2,3], ["key": "value"], .null + ] + try assert(Double.self, fails: fails) + } + + func testDoubleRepresent() { + let node = 124.534.makeNode() + XCTAssert(node == .number(124.534)) + } + + func testFloatInit() throws { + let string = Node("433.1029") + let int = Node(-42) + let double = Node(55.6) + let bool = Node(true) + + try XCTAssert(Float(node: string) == 433.1029) + try XCTAssert(Float(node: int) == -42.0) + try XCTAssert(Float(node: double) == 55.6) + try XCTAssert(Float(node: bool) == 1.0) + + let fails: [Node] = [ + [1,2,3], ["key": "value"], .null + ] + try assert(Float.self, fails: fails) + } + + func testFloatRepresent() { + let float = Float(123.0) + let node = float.makeNode() + XCTAssert(node == .number(123.0)) + } + + func testUnsignedIntegerInit() throws { + let string = Node("400") + let int = Node(42) + let double = Node(55.6) + let bool = Node(true) + + try XCTAssert(UInt(node: string) == 400) + try XCTAssert(UInt(node: int) == 42) + try XCTAssert(UInt(node: double) == 55) + try XCTAssert(UInt(node: bool) == 1) + + let fails: [Node] = [ + [1,2,3], ["key": "value"], .null + ] + try assert(UInt.self, fails: fails) + } + + func testUnsignedIntegerRepresent() throws { + let uint = UInt(124) + let node = try uint.makeNode() + XCTAssert(node == .number(124)) + } + + func testStringInit() throws { + let string = Node("hello :)") + let int = Node(42) + let double = Node(55.6) + let bool = Node(true) + + try XCTAssert(String(node: string) == "hello :)") + try XCTAssert(String(node: int) == "42") + try XCTAssert(String(node: double) == "55.6") + try XCTAssert(String(node: bool) == "true") + + let fails: [Node] = [ + [1,2,3], ["key": "value"], .null + ] + try assert(String.self, fails: fails) + } + + func testStringRepresent() { + let node = "hello :)".makeNode() + XCTAssert(node == .string("hello :)")) + } + + func testNodeConvertible() throws { + let node = Node("hello node") + let initted = try Node(node: node) + let made = node.makeNode() + XCTAssert(initted == made) + } + + private func assert(_ n: N.Type, fails cases: [Node]) throws { + try cases.forEach { fail in + do { + _ = try N(node: fail) + } catch NodeError.unableToConvert(node: _, expected: _) {} + } + } +} diff --git a/Packages/Node-1.0.1/Tests/NodeTests/DictionaryKeyPathTests.swift b/Packages/Node-1.0.1/Tests/NodeTests/DictionaryKeyPathTests.swift new file mode 100644 index 0000000..5dfb2bb --- /dev/null +++ b/Packages/Node-1.0.1/Tests/NodeTests/DictionaryKeyPathTests.swift @@ -0,0 +1,35 @@ +// +// DictionaryKeyPathTests.swift +// Genome +// +// Created by Logan Wright on 7/2/15. +// Copyright © 2015 lowriDevs. All rights reserved. +// + +import XCTest +import Node + +class DictionaryKeyPathTests: XCTestCase { + static let allTests = [ + ("testPaths", testPaths) + ] + + func testPaths() { + let TestDictionary: Node = [ + "one" : [ + "two" : "Found me!" + ] + ] + + var node = TestDictionary + + let path: [String] = ["one", "two"] + let value: String! = node[path]?.string + XCTAssert(value == "Found me!") + + node["path", "to", "new", "value"] = "Hello!" + let setVal = node["path", "to", "new", "value"] + XCTAssert(setVal == "Hello!") + } + +} diff --git a/Packages/Node-1.0.1/Tests/NodeTests/NodeBackedTests.swift b/Packages/Node-1.0.1/Tests/NodeTests/NodeBackedTests.swift new file mode 100644 index 0000000..3e7d7d9 --- /dev/null +++ b/Packages/Node-1.0.1/Tests/NodeTests/NodeBackedTests.swift @@ -0,0 +1,68 @@ +import XCTest +import Node + +struct JSON: NodeBacked { + var node: Node + init(_ node: Node) { + self.node = node + } +} + +class NodeBackedTests: XCTestCase { + static let allTests = [ + ("testSubscripts", testSubscripts), + ("testPolymorphic", testPolymorphic), + ] + + func testSubscripts() throws { + let json = try JSON(node: [ + "names": [ + "", + "", + "World" + ] + ] + ) + + XCTAssertEqual(json["names", 2]?.string, "World") + XCTAssertEqual(json.makeNode(), json.node) + } + + func testPolymorphic() throws { + let node = try JSON( + node: [ + "string": "Hello!", + "int": 3, + "bool": true, + "ob": [ + "name": "World" + ], + "arr": [ + 0, + 1, + 2 + ], + "null": "null", + "double": 3.14 + ] + ) + + XCTAssertEqual(node["string"]?.string, "Hello!") + XCTAssertEqual(node["int"]?.int, 3) + XCTAssertEqual(node["bool"]?.bool, true) + XCTAssertEqual(node["ob", "name"]?.string, "World") + XCTAssertEqual(node["arr", 2]?.int, 2) + XCTAssertEqual(node["null"]?.isNull, true) + XCTAssertEqual(node["double"]?.double, 3.14) + let arr = node["arr"]?.array?.flatMap { $0.int } ?? [] + XCTAssertEqual(arr, [0, 1, 2]) + let ob = node["ob"]?.object + XCTAssertEqual(ob?["name"]?.string, "World") + XCTAssertNil(node["int", "foo"]?.object) + + let jsArr: [JSON] = try [0, 1].map { try $0.converted() } + _ = JSON(jsArr) + let jsOb: [String: JSON] = ["key": JSON(.string("val"))] + _ = JSON(jsOb) + } +} diff --git a/Packages/Node-1.0.1/Tests/NodeTests/NodeDataTypeTests.swift b/Packages/Node-1.0.1/Tests/NodeTests/NodeDataTypeTests.swift new file mode 100644 index 0000000..976ab20 --- /dev/null +++ b/Packages/Node-1.0.1/Tests/NodeTests/NodeDataTypeTests.swift @@ -0,0 +1,57 @@ +// +// NodeDataTypeTest.swift +// Genome +// +// Created by Logan Wright on 12/6/15. +// Copyright © 2015 lowriDevs. All rights reserved. +// + +import XCTest + +@testable import Node + +class NodeDataTypeTests: XCTestCase { + static let allTests = [ + ("testIntegers", testIntegers), + ("testUnsignedIntegers", testUnsignedIntegers), + ] + + // 127 is Int8 max, unless you want to change the way this test is setup, + // the value must be somewhere between 0 and 127 + let integerValue: Int = 127 + lazy var integerNodeValue: Node = .number(Node.Number(self.integerValue)) + + func testIntegers() throws { + let int = try Int(node: integerNodeValue) + XCTAssert(int == integerValue) + + let int8 = try Int8(node: integerNodeValue) + XCTAssert(int8 == Int8(integerValue)) + + let int16 = try Int16(node: integerNodeValue) + XCTAssert(int16 == Int16(integerValue)) + + let int32 = try Int32(node: integerNodeValue) + XCTAssert(int32 == Int32(integerValue)) + + let int64 = try Int64(node: integerNodeValue) + XCTAssert(int64 == Int64(integerValue)) + } + + func testUnsignedIntegers() throws { + let uint = try UInt(node: integerNodeValue) + XCTAssert(uint == UInt(integerValue)) + + let uint8 = try UInt8(node: integerNodeValue) + XCTAssert(uint8 == UInt8(integerValue)) + + let uint16 = try UInt16(node: integerNodeValue) + XCTAssert(uint16 == UInt16(integerValue)) + + let uint32 = try UInt32(node: integerNodeValue) + XCTAssert(uint32 == UInt32(integerValue)) + + let uint64 = try UInt64(node: integerNodeValue) + XCTAssert(uint64 == UInt64(integerValue)) + } +} diff --git a/Packages/Node-1.0.1/Tests/NodeTests/NodeExtractTests.swift b/Packages/Node-1.0.1/Tests/NodeTests/NodeExtractTests.swift new file mode 100644 index 0000000..a1e1cfc --- /dev/null +++ b/Packages/Node-1.0.1/Tests/NodeTests/NodeExtractTests.swift @@ -0,0 +1,245 @@ +// +// BasicTypes.swift +// Genome +// +// Created by Logan Wright on 9/19/15. +// Copyright © 2015 lowriDevs. All rights reserved. +// + +import XCTest +import Foundation +@testable import Node + +struct NoNull: NodeInitializable, Hashable { + let node: Node + + var hashValue: Int { + return "\(node)".hashValue + } + + init(node: Node, in context: Context) throws { + guard node != .null else { + throw NodeError.unableToConvert(node: node, expected: "something not null") + } + + self.node = node + } +} + +func == (l: NoNull, r: NoNull) -> Bool { + return l.node == r.node +} + +class NodeExtractTests: XCTestCase { + static let allTests = [ + ("testExtractTransform", testExtractTransform), + ("testExtractTransformThrows", testExtractTransformThrows), + ("testExtractTransformOptionalValue", testExtractTransformOptionalValue), + ("testExtractTransformOptionalNil", testExtractTransformOptionalNil), + ("testExtractSingle", testExtractSingle), + ("testExtractSingleOptional", testExtractSingleOptional), + ("testExtractSingleThrows", testExtractSingleThrows), + ("testExtractArray", testExtractArray), + ("testExtractArrayOptional", testExtractArrayOptional), + ("testExtractArrayThrows", testExtractArrayThrows), + ("testExtractArrayOfArrays", testExtractArrayOfArrays), + ("testExtractArrayOfArraysOptional", testExtractArrayOfArraysOptional), + ("testExtractArrayOfArraysThrows", testExtractArrayOfArraysThrows), + ("testExtractObject", testExtractObject), + ("testExtractObjectOptional", testExtractObjectOptional), + ("testExtractObjectThrows", testExtractObjectThrows), + ("testExtractObjectOfArrays", testExtractObjectOfArrays), + ("testExtractObjectOfArraysOptional", testExtractObjectOfArraysOptional), + ("testExtractObjectOfArraysThrows", testExtractObjectOfArraysThrows), + ("testExtractSet", testExtractSet), + ("testExtractSetOptional", testExtractSetOptional), + ("testExtractSetThrows", testExtractSetThrows), + ] + + func testExtractTransform() throws { + let node = try Node(node: ["date": 250]) + let extracted = try node.extract("date", transform: Date.fromTimestamp) + XCTAssert(extracted.timeIntervalSince1970 == 250) + } + + func testExtractTransformThrows() throws { + let node = EmptyNode + do { + _ = try node.extract("date", transform: Date.fromTimestamp) + XCTFail("should throw error") + } catch NodeError.unableToConvert {} + } + + func testExtractTransformOptionalValue() throws { + let node = try Node(node: ["date": 250]) + let extracted = try node.extract("date", transform: Date.optionalFromTimestamp) + XCTAssert(extracted?.timeIntervalSince1970 == 250) + } + + func testExtractTransformOptionalNil() throws { + let node = EmptyNode + let extracted = try node.extract("date", transform: Date.optionalFromTimestamp) + XCTAssertNil(extracted) + } + + func testExtractSingle() throws { + let node = try Node(node: ["nest": [ "ed": ["hello": "world", "pi": 3.14159]]]) + let extracted = try node.extract("nest", "ed", "hello") as NoNull + XCTAssert(extracted.node.string == "world") + } + + func testExtractSingleOptional() throws { + let node = try Node(node: ["nest": [ "ed": ["hello": "world", "pi": 3.14159]]]) + let extracted: NoNull? = try node.extract("nest", "ed", "hello") + XCTAssert(extracted?.node.string == "world") + } + + func testExtractSingleThrows() throws { + let node = EmptyNode + do { + _ = try node.extract("nest", "ed", "hello") as NoNull + XCTFail("should throw node error unable to convert") + } catch NodeError.unableToConvert {} + } + + func testExtractArray() throws { + let node = try Node(node: ["nest": [ "ed": ["array": [1, 2, 3, 4]]]]) + let extracted = try node.extract("nest", "ed", "array") as [NoNull] + let numbers = extracted.flatMap { $0.node.int } + XCTAssert(numbers == [1,2,3,4]) + } + + func testExtractArrayOptional() throws { + let node = try Node(node: ["nest": [ "ed": ["array": [1, 2, 3, 4]]]]) + let extracted: [NoNull]? = try node.extract("nest", "ed", "array") + let numbers = extracted?.flatMap { $0.node.int } ?? [] + XCTAssert(numbers == [1,2,3,4]) + } + + func testExtractArrayThrows() throws { + let node = EmptyNode + do { + _ = try node.extract("nest", "ed", "array") as [NoNull] + XCTFail("should throw node error unable to convert") + } catch NodeError.unableToConvert {} + } + + func testExtractArrayOfArrays() throws { + let node = try Node(node: ["nest": [ "ed": ["array": [[1], [2], [3], [4]]]]]) + let extracted = try node.extract("nest", "ed", "array") as [[NoNull]] + let numbers = extracted.map { innerArray in + innerArray.flatMap { $0.node.int } + } + + guard numbers.count == 4 else { + XCTFail("failed array of arrays") + return + } + XCTAssert(numbers[0] == [1]) + XCTAssert(numbers[1] == [2]) + XCTAssert(numbers[2] == [3]) + XCTAssert(numbers[3] == [4]) + } + + func testExtractArrayOfArraysOptional() throws { + let node = try Node(node: ["nest": [ "ed": ["array": [[1], [2], [3], [4]]]]]) + let extracted: [[NoNull]]? = try node.extract("nest", "ed", "array") + let numbers = extracted?.map { innerArray in + innerArray.flatMap { $0.node.int } + } ?? [] + + guard numbers.count == 4 else { + XCTFail("failed array of arrays optional") + return + } + XCTAssert(numbers[0] == [1]) + XCTAssert(numbers[1] == [2]) + XCTAssert(numbers[2] == [3]) + XCTAssert(numbers[3] == [4]) + } + + func testExtractArrayOfArraysThrows() throws { + do { + let node = EmptyNode + _ = try node.extract("nest", "ed", "array") as [[NoNull]] + XCTFail("should throw node error unable to convert") + } catch NodeError.unableToConvert {} + } + + func testExtractObject() throws { + let node = try Node(node: ["nest": [ "ed": ["object": ["hello": "world"]]]]) + let extracted = try node.extract("nest", "ed", "object") as [String: NoNull] + XCTAssert(extracted["hello"]?.node.string == "world") + } + + func testExtractObjectOptional() throws { + let node = try Node(node: ["nest": [ "ed": ["object": ["hello": "world"]]]]) + let extracted: [String: NoNull]? = try node.extract("nest", "ed", "object") + XCTAssert(extracted?["hello"]?.node.string == "world") + } + + func testExtractObjectThrows() throws { + let node = EmptyNode + do { + _ = try node.extract("dont", "exist", 0) as [String: NoNull] + XCTFail("should throw node error unable to convert") + } catch NodeError.unableToConvert {} + } + + func testExtractObjectOfArrays() throws { + let node = try Node(node: ["nest": [ "ed": ["object": ["hello": [1,2,3,4]]]]]) + let extracted = try node.extract("nest", "ed", "object") as [String: [NoNull]] + let ints = extracted["hello"]?.flatMap({ $0.node.int }) ?? [] + XCTAssert(ints == [1,2,3,4]) + } + + func testExtractObjectOfArraysOptional() throws { + let node = try Node(node: ["nest": [ "ed": ["object": ["hello": [1,2,3,4]]]]]) + let extracted: [String: [NoNull]]? = try node.extract("nest", "ed", "object") + let ints = extracted?["hello"]?.flatMap({ $0.node.int }) ?? [] + XCTAssert(ints == [1,2,3,4]) + } + + func testExtractObjectOfArraysThrows() throws { + let node = EmptyNode + do { + _ = try node.extract("dont", "exist", 0) as [String: [NoNull]] + XCTFail("should throw node error unable to convert") + } catch NodeError.unableToConvert {} + } + + func testExtractSet() throws { + let node = try Node(node: ["nest": [ "ed": ["array": [1, 2, 3, 4]]]]) + let extracted = try node.extract("nest", "ed", "array") as Set + let ints = [1,2,3,4] + let compare = try ints.map(to: NoNull.self).set + XCTAssert(extracted == compare) + } + + func testExtractSetOptional() throws { + let node = try Node(node: ["nest": [ "ed": ["array": [1, 2, 3, 4]]]]) + let extracted: Set? = try node.extract("nest", "ed", "array") + let ints = [1,2,3,4] + let compare = try ints.map(to: NoNull.self).set + XCTAssert(extracted == compare) + } + + func testExtractSetThrows() throws { + let node = EmptyNode + do { + _ = try node.extract("dont", "exist", 0) as Set + XCTFail("should throw node error unable to convert") + } catch NodeError.unableToConvert {} + } +} + +extension Date { + static func fromTimestamp(_ timestamp: Int) -> Date { + return Date(timeIntervalSince1970: TimeInterval(timestamp)) + } + + static func optionalFromTimestamp(_ timestamp: Int?) -> Date? { + guard let stamp = timestamp else { return nil } + return fromTimestamp(stamp) + } +} diff --git a/Packages/Node-1.0.1/Tests/NodeTests/NodeIndexableTests.swift b/Packages/Node-1.0.1/Tests/NodeTests/NodeIndexableTests.swift new file mode 100644 index 0000000..7967554 --- /dev/null +++ b/Packages/Node-1.0.1/Tests/NodeTests/NodeIndexableTests.swift @@ -0,0 +1,60 @@ +// +// BasicTypes.swift +// Genome +// +// Created by Logan Wright on 9/19/15. +// Copyright © 2015 lowriDevs. All rights reserved. +// + +import XCTest +@testable import Node + +class NodeIndexableTests: XCTestCase { + static let allTests = [ + ("testInt", testInt), + ("testString", testString), + ("testStringSequenceObject", testStringSequenceObject), + ("testStringSequenceArray", testStringSequenceArray), + ("testIntSequence", testIntSequence), + ("testMixed", testMixed), + ] + + func testInt() { + let array: Node = ["one", + "two", + "three"] + let path = [1] + XCTAssert(array[path] == "two") + } + + func testString() { + let object: Node = ["a" : 1] + XCTAssert(object["a"] == 1) + } + + func testStringSequenceObject() { + let ob: Node = ["key" : ["path" : "found me!"]] + XCTAssert(ob["key", "path"] == "found me!") + } + + func testStringSequenceArray() { + let obArray: Node = [["a" : 0], + ["a" : 1], + ["a" : 2], + ["a" : 3]] + let collection = obArray["a"] + XCTAssert(collection == [0,1,2,3]) + } + + func testIntSequence() { + let inner: Node = ["...", + "found me!"] + let outer: Node = [inner] + XCTAssert(outer[0, 1] == "found me!") + } + + func testMixed() { + let mixed: Node = ["one" : ["a", "b", "c"]] + XCTAssert(mixed["one", 1] == "b") + } +} diff --git a/Packages/Node-1.0.1/Tests/NodeTests/NodePolymorphicTests.swift b/Packages/Node-1.0.1/Tests/NodeTests/NodePolymorphicTests.swift new file mode 100644 index 0000000..8fccb56 --- /dev/null +++ b/Packages/Node-1.0.1/Tests/NodeTests/NodePolymorphicTests.swift @@ -0,0 +1,241 @@ +// +// NodeEquatableTests.swift +// Node +// +// Created by Logan Wright on 7/20/16. +// +// + +import XCTest +@testable import Node + +class NodePolymorphicTests: XCTestCase { + static let allTests = [ + ("testPolymorphicString", testPolymorphicString), + ("testPolymorphicInt", testPolymorphicInt), + ("testPolymorphicUInt", testPolymorphicUInt), + ("testPolymorphicFloat", testPolymorphicFloat), + ("testPolymorphicDouble", testPolymorphicDouble), + ("testPolymorphicNull", testPolymorphicNull), + ("testPolymorphicBool", testPolymorphicBool), + ("testPolymorphicArray", testPolymorphicArray), + ("testPolymorphicObject", testPolymorphicObject) + ] + + func testPolymorphicString() { + let bool: Node = true + let int: Node = 1 + let double: Node = 3.14 + let string: Node = "hi" + let ob: Node = .object(["key": "value"]) + let arr: Node = .array([1,2,3]) + let bytes: Node = .bytes([10, 20, 30, 40]) + + XCTAssert(bool.string == "true") + XCTAssert(int.string == "1") + XCTAssert(double.string == "3.14") + XCTAssert(string.string == "hi") + XCTAssertNil(ob.string) + XCTAssertNil(arr.string) + XCTAssertNil(bytes.string) + } + + func testPolymorphicInt() { + let boolTrue: Node = true + let boolFalse: Node = false + let int: Node = 42 + let double: Node = 3.14 + let intString: Node = "123" + + let histring: Node = "hi" + let ob: Node = .object(["key": "value"]) + let arr: Node = .array([1,2,3]) + let bytes: Node = .bytes([10, 20, 30, 40]) + + XCTAssert(boolTrue.int == 1) + XCTAssert(boolFalse.int == 0) + XCTAssert(int.int == 42) + XCTAssert(double.int == 3) + XCTAssert(intString.int == 123) + XCTAssertNil(histring.int) + XCTAssertNil(ob.int) + XCTAssertNil(arr.int) + XCTAssertNil(bytes.int) + } + + func testPolymorphicUInt() { + let boolTrue: Node = true + let boolFalse: Node = false + let int: Node = 42 + let double: Node = 3.14 + let intString: Node = "123" + + let histring: Node = "hi" + let ob: Node = .object(["key": "value"]) + let arr: Node = .array([1,2,3]) + let bytes: Node = .bytes([10, 20, 30, 40]) + + XCTAssert(boolTrue.uint == 1) + XCTAssert(boolFalse.uint == 0) + XCTAssert(int.uint == 42) + XCTAssert(double.uint == 3) + XCTAssert(intString.uint == 123) + XCTAssertNil(histring.uint) + XCTAssertNil(ob.uint) + XCTAssertNil(arr.uint) + XCTAssertNil(bytes.uint) + } + + func testPolymorphicFloat() { + let boolTrue: Node = true + let boolFalse: Node = false + let int: Node = 42 + let double: Node = 3.14 + let intString: Node = "123" + let doubleString: Node = "42.5997" + + let histring: Node = "hi" + let ob: Node = .object(["key": "value"]) + let arr: Node = .array([1,2,3]) + let bytes: Node = .bytes([10, 20, 30, 40]) + + XCTAssert(boolTrue.float == 1) + XCTAssert(boolFalse.float == 0) + XCTAssert(int.float == 42) + XCTAssert(double.float == 3.14) + XCTAssert(intString.float == 123) + XCTAssert(doubleString.float == 42.5997) + XCTAssertNil(histring.float) + XCTAssertNil(ob.float) + XCTAssertNil(arr.float) + XCTAssertNil(bytes.float) + } + + func testPolymorphicDouble() { + let boolTrue: Node = true + let boolFalse: Node = false + let int: Node = 42 + let double: Node = 3.14 + let intString: Node = "123" + let doubleString: Node = "42.5997" + + let histring: Node = "hi" + let ob: Node = .object(["key": "value"]) + let arr: Node = .array([1,2,3]) + let bytes: Node = .bytes([10, 20, 30, 40]) + + XCTAssert(boolTrue.double == 1) + XCTAssert(boolFalse.double == 0) + XCTAssert(int.double == 42) + XCTAssert(double.double == 3.14) + XCTAssert(intString.double == 123) + XCTAssert(doubleString.double == 42.5997) + XCTAssertNil(histring.double) + XCTAssertNil(ob.double) + XCTAssertNil(arr.double) + XCTAssertNil(bytes.double) + } + + func testPolymorphicNull() { + let null: Node = .null + let lowerNullString: Node = "null" + let upperNullString: Node = "NULL" + + let bool: Node = true + let int: Node = 42 + let double: Node = 3.14 + let string: Node = "hi" + let ob: Node = .object(["key": "value"]) + let arr: Node = .array([1,2,3]) + let bytes: Node = .bytes([10, 20, 30, 40]) + + XCTAssertTrue(null.isNull) + XCTAssertTrue(lowerNullString.isNull) + XCTAssertTrue(upperNullString.isNull) + + XCTAssertFalse(bool.isNull) + XCTAssertFalse(int.isNull) + XCTAssertFalse(double.isNull) + XCTAssertFalse(string.isNull) + XCTAssertFalse(ob.isNull) + XCTAssertFalse(arr.isNull) + XCTAssertFalse(bytes.isNull) + } + + func testPolymorphicBool() { + let null: Node = .null + let bool: Node = true + let int: Node = 42 + let boolInt: Node = 1 + let double: Node = 3.14 + let boolDouble: Node = 1.0 + let string: Node = "hi" + let boolString: Node = "true" + let ob: Node = .object(["key": "value"]) + let arr: Node = .array([1,2,3]) + let bytes: Node = .bytes([10, 20, 30, 40]) + + XCTAssert(null.bool == false) + XCTAssert(bool.bool == true) + XCTAssertNil(int.bool) + XCTAssert(boolInt.bool == true) + XCTAssertNil(double.bool) + XCTAssert(boolDouble.bool == true) + XCTAssertNil(string.bool) + XCTAssert(boolString.bool == true) + XCTAssertNil(ob.bool) + XCTAssertNil(arr.bool) + XCTAssertNil(bytes.bool) + } + + func testPolymorphicArray() { + let null: Node = .null + let bool: Node = true + let int: Node = 42 + let double: Node = 3.14 + let string: Node = "hi" + let arrayString: Node = "hi, there, array" + let ob: Node = .object(["key": "value"]) + let arr: Node = .array([1,2,3]) + let bytes: Node = .bytes([10, 20, 30, 40]) + + XCTAssertNil(null.array) + XCTAssertNil(bool.array) + XCTAssertNil(int.array) + XCTAssertNil(double.array) + + let single = string.array?.flatMap { $0.string } ?? [] + XCTAssert(single == ["hi"]) + let fetched = arrayString.array?.flatMap { $0.string } ?? [] + XCTAssert(fetched == ["hi", "there", "array"]) + let array = arr.array?.flatMap { $0.int } ?? [] + XCTAssert(array == [1, 2, 3]) + + XCTAssertNil(ob.array) + XCTAssertNil(bytes.array) + } + + func testPolymorphicObject() { + let null: Node = .null + let bool: Node = true + let int: Node = 42 + let double: Node = 3.14 + let string: Node = "hi" + let ob: Node = .object(["key": "value"]) + let arr: Node = .array([1,2,3]) + let bytes: Node = .bytes([10, 20, 30, 40]) + + XCTAssertNotNil(ob.object) + XCTAssert(ob.object?["key"]?.string == "value") + + XCTAssertNil(null.object) + XCTAssertNil(bool.object) + XCTAssertNil(int.object) + XCTAssertNil(double.object) + XCTAssertNil(string.object) + XCTAssertNil(arr.object) + XCTAssertNil(bytes.object) + + } + +} diff --git a/Packages/Node-1.0.1/Tests/NodeTests/NodeTests.swift b/Packages/Node-1.0.1/Tests/NodeTests/NodeTests.swift new file mode 100644 index 0000000..d5fb771 --- /dev/null +++ b/Packages/Node-1.0.1/Tests/NodeTests/NodeTests.swift @@ -0,0 +1,141 @@ +// +// NodeEquatableTests.swift +// Node +// +// Created by Logan Wright on 7/20/16. +// +// + +import XCTest +@testable import Node + +class NodeTests: XCTestCase { + static let allTests = [ + ("testInits", testInits), + ("testArrayInits", testArrayInits), + ("testObjectInits", testObjectInits), + ("testNonHomogenousArrayInits", testNonHomogenousArrayInits), + ("testNonHomogenousObjectInits", testNonHomogenousObjectInits), + ("testLiterals", testLiterals), + ("testEquatable", testEquatable), + ] + + func testInits() { + // these are mostly here to ensure compilation errors don't occur + XCTAssert(Node(true) == .bool(true)) + XCTAssert(Node("hi") == .string("hi")) + XCTAssert(Node(1) == .number(1)) + XCTAssert(Node(3.14) == .number(3.14)) + + let uint = UInt(42) + XCTAssert(Node(uint) == .number(Node.Number(uint))) + + let number = Node.Number(2345) + XCTAssert(Node(number) == .number(number)) + + let array = [Node(1), Node(2), Node(3)] + XCTAssert(Node(array) == .array([1,2,3])) + + let object = ["key": Node("value")] + XCTAssert(Node(object) == .object(object)) + + XCTAssert(Node(bytes: [1,2,3,4]) == .bytes([1,2,3,4])) + } + + func testArrayInits() throws { + let array: [Int] = [1,2,3,4,5] + let node = try Node(node: array) + XCTAssert(node == [1,2,3,4,5]) + + let optionalArray: [String?] = ["a", "b", "c", nil, "d", nil] + let optionalNode = try Node.init(node: optionalArray) + XCTAssertEqual(optionalNode, ["a", "b", "c", .null, "d", .null]) + } + + func testObjectInits() throws { + let dict: [String: String] = [ + "hello": "world", + "goodbye": "moon" + ] + let node = try Node(node: dict) + XCTAssert(node == ["hello": "world", "goodbye": "moon"]) + + let optionalDict: [String: String?] = [ + "hello": "world", + "goodbye": nil + ] + let optionalNode = try Node(node: optionalDict) + XCTAssertEqual(optionalNode, ["hello": "world", "goodbye": .null]) + } + + func testNonHomogenousArrayInits() throws { + let array: [NodeRepresentable] = [1, "hiya", Node.object(["a": "b"]), false] + let node = try Node(node: array) + XCTAssertEqual(node, [1, "hiya", Node.object(["a": "b"]), false]) + + + let optionalArray: [NodeRepresentable?] = [42, "bye", Node.array([1,2,3]), true, nil] + let optionalNode = try Node(node: optionalArray) + XCTAssertEqual(optionalNode, [42, "bye", Node.array([1,2,3]), true, .null]) + } + + func testNonHomogenousObjectInits() throws { + let dict: [String: NodeRepresentable] = [ + "hello": "world", + "goodbye": 1 + ] + let node = try Node(node: dict) + XCTAssertEqual(node, ["hello": "world", "goodbye": 1]) + + let optionalDict: [String: NodeRepresentable?] = [ + "hello": "world", + "goodbye": nil, + "ok": 1 + ] + let optionalNode = try Node(node: optionalDict) + XCTAssertEqual(optionalNode, ["hello": "world", "goodbye": .null, "ok": 1]) + } + + func testLiterals() { + XCTAssert(Node.null == nil) + XCTAssert(Node.bool(false) == false) + XCTAssert(Node.number(1) == 1) + XCTAssert(Node.number(42.3) == 42.3) + + XCTAssert(Node.string("test") == "test") + let unicode = Node(unicodeScalarLiteral: "test") + XCTAssert(Node.string("test") == unicode) + let grapheme = Node(extendedGraphemeClusterLiteral: "test") + XCTAssert(Node.string("test") == grapheme) + + XCTAssert(Node.array([1,2,3]) == [1,2,3]) + XCTAssert(Node.object(["key": "value"]) == ["key": "value"]) + } + + func testEquatable() { + let truthyPairs: [(Node, Node)] = [ + (nil, nil), + (1, 1.0), + (true, true), + (false, false), + ("hello", "hello"), + ([1,2,3], [1,2,3]), + (["key": "value"], ["key": "value"]) + ] + + truthyPairs.forEach { lhs, rhs in XCTAssert(lhs == rhs, "\(lhs) should equal \(rhs)") } + + let falsyPairs: [(Node, Node)] = [ + (nil, 42), + (1, "hello"), + (true, ["key": "value"]), + ([1,2,3], false), + ("hello", "goodbye"), + ([1,2,3], [1,2,3,4]), + (["key": "value"], ["array", "of", "strings"]) + ] + + falsyPairs.forEach { lhs, rhs in XCTAssert(lhs != rhs, "\(lhs) should equal \(rhs)") } + } + +} diff --git a/Packages/Node-1.0.1/Tests/NodeTests/NumberTests.swift b/Packages/Node-1.0.1/Tests/NodeTests/NumberTests.swift new file mode 100644 index 0000000..ea814c0 --- /dev/null +++ b/Packages/Node-1.0.1/Tests/NodeTests/NumberTests.swift @@ -0,0 +1,145 @@ +// +// NumberTests.swift +// Node +// +// Created by Logan Wright on 7/20/16. +// +// + +import XCTest +@testable import Node + +class NumberTests: XCTestCase { + static let allTests = [ + ("testSignedInit", testSignedInit), + ("testUnsignedInit", testUnsignedInit), + ("testFloatingPoint", testFloatingPoint), + ("testAccessors", testAccessors), + ("testBoolAccessors", testBoolAccessors), + ("testIntMax", testIntMax), + ("testDescriptions", testDescriptions), + ("testEquatableTrue", testEquatableTrue), + ("testEquatableFalse", testEquatableFalse), + ] + + func testSignedInit() { + let a = Node.Number(Int8(1)) + let b = Node.Number(Int16(-2)) + let c = Node.Number(Int32(3)) + let d = Node.Number(Int(-4)) + + XCTAssert([a, b, c, d] == [1, -2, 3, -4]) + } + + func testUnsignedInit() { + let a = Node.Number(UInt8(1)) + let b = Node.Number(UInt16(2)) + let c = Node.Number(UInt32(3)) + let d = Node.Number(UInt(4)) + + XCTAssert([a, b, c, d] == [1, 2, 3, 4]) + } + + func testFloatingPoint() { + let double = Double(52.899) + let float = Float(10.5) + + XCTAssert(Node.Number(double) == 52.899) + XCTAssert(Node.Number(float) == 10.5) + } + + func testAccessors() { + let intRaw = Int(-42) + let doubleRaw = Double(52.8) + let uintRaw = UInt(3000) + + let int = Node.Number(intRaw) + XCTAssert(int.int == -42) + XCTAssert(int.double == -42.0) + XCTAssert(int.uint == 0) + + let double = Node.Number(doubleRaw) + XCTAssert(double.int == 52) + XCTAssert(double.double == 52.8) + XCTAssert(double.uint == 52) + + let uint = Node.Number(uintRaw) + XCTAssert(uint.int == 3000) + XCTAssert(uint.double == 3000.0) + XCTAssert(uint.uint == 3000) + } + + func testBoolAccessors() { + let intTrue = Int(1) + let doubleTrue = Double(1.0) + let uintTrue = UInt(1) + XCTAssert(Node.Number(intTrue).bool == true) + XCTAssert(Node.Number(doubleTrue).bool == true) + XCTAssert(Node.Number(uintTrue).bool == true) + + let intFalse = Int(0) + let doubleFalse = Double(0.0) + let uintFalse = UInt(0) + XCTAssert(Node.Number(intFalse).bool == false) + XCTAssert(Node.Number(doubleFalse).bool == false) + XCTAssert(Node.Number(uintFalse).bool == false) + + let intNil = Int(-6) + let doubleNil = Double(9.98) + let uintNil = UInt(899999) + XCTAssertNil(Node.Number(intNil).bool) + XCTAssertNil(Node.Number(doubleNil).bool) + XCTAssertNil(Node.Number(uintNil).bool) + } + + func testIntMax() { + let exceed = UInt.intMax + 50 + let number = Node.Number(exceed) + XCTAssert(number.int == Int.max) + XCTAssert(number.uint == exceed) + } + + func testDescriptions() { + let int = Int(-6) + let double = Double(9.98) + let uint = UInt(899999) + + XCTAssert(Node.Number(int).description == "-6") + XCTAssert(Node.Number(double).description == "9.98") + XCTAssert(Node.Number(uint).description == "899999") + } + + func testEquatableTrue() { + let int = Node.Number(Int(88)) + let double = Node.Number(Double(88)) + let uint = Node.Number(UInt(88)) + + XCTAssert(int == int) + XCTAssert(int == double) + XCTAssert(int == uint) + + XCTAssert(double == int) + XCTAssert(double == double) + XCTAssert(double == uint) + + XCTAssert(uint == int) + XCTAssert(uint == double) + XCTAssert(uint == uint) + } + + func testEquatableFalse() { + let int = Node.Number(Int(-1)) + let double = Node.Number(Double(99.8)) + let uint = Node.Number(UInt(9632)) + + XCTAssert(int != double) + XCTAssert(int != uint) + + XCTAssert(double != int) + XCTAssert(double != uint) + + XCTAssert(uint != int) + XCTAssert(uint != double) + } + +} diff --git a/Packages/Node-1.0.1/Tests/NodeTests/SequenceConvertibleTests.swift b/Packages/Node-1.0.1/Tests/NodeTests/SequenceConvertibleTests.swift new file mode 100644 index 0000000..ddd6e80 --- /dev/null +++ b/Packages/Node-1.0.1/Tests/NodeTests/SequenceConvertibleTests.swift @@ -0,0 +1,164 @@ +// +// SequenceConvertibleTests.swift +// Node +// +// Created by Logan Wright on 7/20/16. +// +// + +import XCTest +import Node + +class TestInitializable: NodeInitializable { + let node: Node + + required init(node: Node, in context: Context) { + self.node = node + } +} + +final class Foo: NodeConvertible { + var node: Node + var contextMakeNode: Context? + + init(node: Node, in context: Context) throws { + self.node = node + } + + func makeNode(context: Context = EmptyNode) throws -> Node { + self.contextMakeNode = context + return node + } +} + +class SequenceConvertibleTests: XCTestCase { + static let allTests = [ + ("testSequence", testSequence), + ("testDictionary", testDictionary), + ("testArrayConvert", testArrayConvert), + ("testSetConvert", testSetConvert), + ] + + func testSequence() throws { + let ints: [Int] = [1,2,3,4,5] + let node = try ints.makeNode() + XCTAssert(node == .array([1,2,3,4,5])) + + let representables = ints.map { $0 as NodeRepresentable } + let node2 = try representables.makeNode() + XCTAssert(node2 == .array([1,2,3,4,5])) + + let models = try ints.converted(to: [TestInitializable].self) + let backInts = models.map { $0.node } .flatMap { $0.int } + XCTAssert(backInts == ints) + + let models2 = try representables.converted(to: [TestInitializable].self) + let backInts2 = models2.map { $0.node } .flatMap { $0.int } + XCTAssert(backInts2 == ints) + + + // This tests whether the context is passed to the sequence + let foo1 = try Foo(node: [ + "hello" + ]) + let foo2 = try Foo(node: [ + "goodbye" + ]) + + XCTAssertNil(foo1.contextMakeNode) + XCTAssertNil(foo2.contextMakeNode) + + let context = ["isContext": true] + + let _ = try [foo1, foo2].makeNode(context: context) + + guard let foo1Context = foo1.contextMakeNode as? [String: Bool], + let foo2Context = foo1.contextMakeNode as? [String: Bool] else { + XCTFail() + return + } + + XCTAssert(foo1Context == context) + XCTAssert(foo2Context == context) + + } + + func testDictionary() throws { + let dict: [String: String] = [ + "key": "val", + "hi": "world" + ] + let node = try dict.makeNode() + XCTAssert(node == ["key": "val", "hi": "world"]) + + let model = try dict.converted(to: TestInitializable.self) + XCTAssert(model.node["key"]?.string == "val") + XCTAssert(model.node["hi"]?.string == "world") + } + + func testArrayConvert() throws { + let ints = try [Int](node: Node.array([1,2,3,4,"5"])) + XCTAssert(ints == [1,2,3,4,5]) + + let one = try [Int](node: 1) + XCTAssert(one == [1]) + + let strings = ["1", "2", "3", "4", "5"] + let collected = try strings.map(to: Int.self) + XCTAssert(collected == [1,2,3,4,5]) + + let collectedMixed = try [1, 2, "3", "4", 5].map(to: Int.self) + XCTAssert(collectedMixed == [1,2,3,4,5]) + } + + func testSetConvert() throws { + let ints = try Set(node: Node.array([1,2,3,4,"5"])) + XCTAssert(ints == [1,2,3,4,5]) + + let one = try Set(node: 1) + XCTAssert(one == [1]) + + let strings = ["1", "2", "3", "4", "5"] + let collected = try Set(node: Node(node: strings)) + XCTAssert(collected == [1,2,3,4,5]) + + let collectedMixed = try [1, 2, "3", "4", 5].map(to: Int.self).set + XCTAssert(collectedMixed == [1,2,3,4,5]) + } + + func testRepresentableDictionary() throws { + let node = try Node(node: [ + "hello": 52, + ]) + XCTAssertEqual(node, .object(["hello": 52])) + + let foo = try Foo(node: [ + "hello": 52 + ]) + XCTAssertEqual(foo.node, .object(["hello": 52])) + + let fooWithNil = try Foo(node: [ + "hello": nil + ]) + XCTAssertEqual(fooWithNil.node, .object(["hello": .null])) + } + + func testRepresentableArray() throws { + let node = try Node(node: [ + "hello", + ]) + XCTAssertEqual(node, .array(["hello"])) + + + let foo = try Foo(node: [ + "hello" + ]) + XCTAssertEqual(foo.node, .array(["hello"])) + + let fooWithNil = try Foo(node: [ + nil + ]) + XCTAssertEqual(fooWithNil.node, .array([.null])) + + } +} diff --git a/Packages/Node-1.0.1/circle.yml b/Packages/Node-1.0.1/circle.yml new file mode 100644 index 0000000..edd2fba --- /dev/null +++ b/Packages/Node-1.0.1/circle.yml @@ -0,0 +1,3 @@ +test: + override: + - eval "$(curl -sL swift.vapor.sh/ci)" diff --git a/Packages/PathIndexable-1.0.0/.gitignore b/Packages/PathIndexable-1.0.0/.gitignore new file mode 100644 index 0000000..3850f01 --- /dev/null +++ b/Packages/PathIndexable-1.0.0/.gitignore @@ -0,0 +1,39 @@ +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +*.xcsmblueprint + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control +# +Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +Carthage/Checkouts + +Carthage/Build + +# Swift Package Manager +.build/ +Packages/ +*.xcodeproj diff --git a/Packages/PathIndexable-1.0.0/.travis.yml b/Packages/PathIndexable-1.0.0/.travis.yml new file mode 100644 index 0000000..6f74cb2 --- /dev/null +++ b/Packages/PathIndexable-1.0.0/.travis.yml @@ -0,0 +1,10 @@ +os: + - linux + - osx +language: generic +sudo: required +dist: trusty +osx_image: xcode8 +script: + - eval "$(curl -sL swift.vapor.sh/ci)" + - eval "$(curl -sL swift.vapor.sh/codecov)" diff --git a/Packages/PathIndexable-1.0.0/LICENSE b/Packages/PathIndexable-1.0.0/LICENSE new file mode 100644 index 0000000..242132d --- /dev/null +++ b/Packages/PathIndexable-1.0.0/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Packages/PathIndexable-1.0.0/Package.swift b/Packages/PathIndexable-1.0.0/Package.swift new file mode 100644 index 0000000..ce4eec9 --- /dev/null +++ b/Packages/PathIndexable-1.0.0/Package.swift @@ -0,0 +1,5 @@ +import PackageDescription + +let package = Package( + name: "PathIndexable" +) diff --git a/Packages/PathIndexable-1.0.0/README.md b/Packages/PathIndexable-1.0.0/README.md new file mode 100644 index 0000000..be2f331 --- /dev/null +++ b/Packages/PathIndexable-1.0.0/README.md @@ -0,0 +1,25 @@ +# PathIndexable + +![Swift](http://img.shields.io/badge/swift-3.0-brightgreen.svg) +[![Build Status](https://travis-ci.org/vapor/core.svg?branch=master)](https://travis-ci.org/vapor/path-indexable) +[![CircleCI](https://circleci.com/gh/vapor/core.svg?style=shield)](https://circleci.com/gh/vapor/path-indexable) +[![Code Coverage](https://codecov.io/gh/vapor/core/branch/master/graph/badge.svg)](https://codecov.io/gh/vapor/path-indexable) +[![Codebeat](https://codebeat.co/badges/a793ad97-47e3-40d9-82cf-2aafc516ef4e)](https://codebeat.co/projects/github-com-vapor-path-indexable) +[![Slack Status](http://vapor.team/badge.svg)](http://vapor.team) + + +The purpose of this package is to allow complex key path logic to be applied to multiple types of data structures. + +This type is used to define a structure that can inherit complex subscripting. + +## 📖 Documentation + +Visit the Vapor web framework's [documentation](http://docs.vapor.codes) for instructions on how to use this package. + +## 💧 Community + +Join the welcoming community of fellow Vapor developers in [slack](http://vapor.team). + +## 🔧 Compatibility + +This package has been tested on macOS and Ubuntu. diff --git a/Packages/PathIndexable-1.0.0/Sources/PathIndexable/PathIndexable+Subscripting.swift b/Packages/PathIndexable-1.0.0/Sources/PathIndexable/PathIndexable+Subscripting.swift new file mode 100644 index 0000000..c5fa9d4 --- /dev/null +++ b/Packages/PathIndexable-1.0.0/Sources/PathIndexable/PathIndexable+Subscripting.swift @@ -0,0 +1,108 @@ +// +// Genome +// +// Created by Logan Wright +// Copyright © 2016 lowriDevs. All rights reserved. +// +// MIT +// + +// MARK: Subscripts + +extension PathIndexable { + public subscript(indexes: PathIndex...) -> Self? { + get { + return self[indexes] + } + set { + self[indexes] = newValue + } + } + + public subscript(indexes: [PathIndex]) -> Self? { + get { + let first: Optional = self + return indexes.reduce(first) { next, index in + guard let next = next else { return nil } + return index.access(in: next) + } + } + set { + var keys = indexes + guard let first = keys.first else { return } + keys.remove(at: 0) + + if keys.isEmpty { + first.set(newValue, to: &self) + } else { + var next = self[first] ?? first.makeEmptyStructure() as Self + next[keys] = newValue + self[first] = next + } + } + } +} + +extension PathIndexable { + public subscript(indexes: Int...) -> Self? { + get { + return self[indexes] + } + set { + self[indexes] = newValue + } + } + + public subscript(indexes: [Int]) -> Self? { + get { + let indexable = indexes.map { $0 as PathIndex } + return self[indexable] + } + set { + let indexable = indexes.map { $0 as PathIndex } + self[indexable] = newValue + } + } +} + +extension PathIndexable { + public subscript(path path: String) -> Self? { + get { + let comps = path.characters.split(separator: ".").map(String.init) + return self[comps] + } + set { + let comps = path.keyPathComponents() + self[comps] = newValue + } + } + + public subscript(indexes: String...) -> Self? { + get { + return self[indexes] + } + set { + self[indexes] = newValue + } + } + + public subscript(indexes: [String]) -> Self? { + get { + let indexable = indexes.map { $0 as PathIndex } + return self[indexable] + } + set { + let indexable = indexes.map { $0 as PathIndex } + self[indexable] = newValue + } + } + +} + +extension String { + internal func keyPathComponents() -> [String] { + return characters + .split(separator: ".") + .map(String.init) + } +} diff --git a/Packages/PathIndexable-1.0.0/Sources/PathIndexable/PathIndexable.swift b/Packages/PathIndexable-1.0.0/Sources/PathIndexable/PathIndexable.swift new file mode 100644 index 0000000..414ab5c --- /dev/null +++ b/Packages/PathIndexable-1.0.0/Sources/PathIndexable/PathIndexable.swift @@ -0,0 +1,154 @@ + +/** + Objects wishing to inherit complex subscripting should implement + this protocol + */ +public protocol PathIndexable { + /// If self is an array representation, return array + var pathIndexableArray: [Self]? { get } + + /// If self is an object representation, return object + var pathIndexableObject: [String: Self]? { get } + + /** + Initialize a new object encapsulating an array of Self + + - parameter array: value to encapsulate + */ + init(_ array: [Self]) + + /** + Initialize a new object encapsulating an object of type [String: Self] + + - parameter object: value to encapsulate + */ + init(_ object: [String: Self]) +} + +// MARK: Indexable + +/** + Anything that can be used as subscript access for a Node. + + Int and String are supported natively, additional Indexable types + should only be added after very careful consideration. + */ +public protocol PathIndex { + /** + Acess for 'self' within the given node, + ie: inverse ov `= node[self]` + + - parameter node: the node to access + + - returns: a value for index of 'self' if exists + */ + func access(in node: T) -> T? + + /** + Set given input to a given node for 'self' if possible. + ie: inverse of `node[0] =` + + - parameter input: value to set in parent, or `nil` if should remove + - parameter parent: node to set input in + */ + func set(_ input: T?, to parent: inout T) + + /** + Create an empty structure that can be set with the given type. + + ie: + - a string will create an empty dictionary to add itself as a value + - an Int will create an empty array to add itself as a value + + - returns: an empty structure that can be set by Self + */ + func makeEmptyStructure() -> T +} + +extension Int: PathIndex { + /** + - see: PathIndex + */ + public func access(in node: T) -> T? { + guard + let array = node.pathIndexableArray, + self < array.count + else { + return nil + } + + return array[self] + } + + /** + - see: PathIndex + */ + public func set(_ input: T?, to parent: inout T) { + guard + let array = parent.pathIndexableArray, + self < array.count + else { + return + } + + var mutable = array + if let new = input { + mutable[self] = new + } else { + mutable.remove(at: self) + } + parent = type(of: parent).init(mutable) + } + + public func makeEmptyStructure() -> T { + return T([]) + } +} + +extension String: PathIndex { + /** + - see: PathIndex + */ + public func access(in node: T) -> T? { + if let object = node.pathIndexableObject?[self] { + return object + } else if let array = node.pathIndexableArray { + // Index takes precedence + if let idx = Int(self), idx < array.count { + return array[idx] + } + + let value = array.flatMap(self.access) + if value.count > 0 { + return type(of: node).init(value) + } + + return nil + } + + return nil + } + + /** + - see: PathIndex + */ + public func set(_ input: T?, to parent: inout T) { + if let object = parent.pathIndexableObject { + var mutable = object + mutable[self] = input + parent = type(of: parent).init(mutable) + } else if let array = parent.pathIndexableArray { + let mapped: [T] = array.map { val in + var mutable = val + self.set(input, to: &mutable) + return mutable + } + parent = type(of: parent).init(mapped) + } + } + + + public func makeEmptyStructure() -> T { + return T([:]) + } +} diff --git a/Packages/PathIndexable-1.0.0/Tests/Info.plist b/Packages/PathIndexable-1.0.0/Tests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/Packages/PathIndexable-1.0.0/Tests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Packages/PathIndexable-1.0.0/Tests/LinuxMain.swift b/Packages/PathIndexable-1.0.0/Tests/LinuxMain.swift new file mode 100644 index 0000000..84034d6 --- /dev/null +++ b/Packages/PathIndexable-1.0.0/Tests/LinuxMain.swift @@ -0,0 +1,12 @@ + +#if os(Linux) + +import XCTest +@testable import PathIndexableTests + +XCTMain([ + testCase(DictionaryKeyPathTests.allTests), + testCase(PathIndexableTests.allTests), +]) + +#endif diff --git a/Packages/PathIndexable-1.0.0/Tests/PathIndexableTests/DictionaryKeyPathTests.swift b/Packages/PathIndexable-1.0.0/Tests/PathIndexableTests/DictionaryKeyPathTests.swift new file mode 100644 index 0000000..37c7a61 --- /dev/null +++ b/Packages/PathIndexable-1.0.0/Tests/PathIndexableTests/DictionaryKeyPathTests.swift @@ -0,0 +1,47 @@ +// +// DictionaryKeyPathTests.swift +// Genome +// +// Created by Logan Wright on 7/2/15. +// Copyright © 2015 lowriDevs. All rights reserved. +// + +import XCTest +import PathIndexable + +class DictionaryKeyPathTests: XCTestCase { + static var allTests = [ + ("testPaths", testPaths) + ] + + func testPaths() { + let inner = Node(["two" : .string("Found me!")]) + var test = Node([ + "one" : inner + ]) + + guard let node = test["one", "two"] else { + XCTFail() + return + } + + guard case let .string(str) = node else { + XCTFail() + return + } + XCTAssert(str == "Found me!") + + test["path", "to", "new", "value"] = .string("Hello!") + guard let setVal = test["path", "to", "new", "value"] else { + XCTFail() + return + } + guard case let .string(setStr) = setVal else { + XCTFail() + return + } + + XCTAssert(setStr == "Hello!") + } + +} diff --git a/Packages/PathIndexable-1.0.0/Tests/PathIndexableTests/PathIndexableTests.swift b/Packages/PathIndexable-1.0.0/Tests/PathIndexableTests/PathIndexableTests.swift new file mode 100644 index 0000000..5340146 --- /dev/null +++ b/Packages/PathIndexable-1.0.0/Tests/PathIndexableTests/PathIndexableTests.swift @@ -0,0 +1,263 @@ +// +// BasicTypes.swift +// Genome +// +// Created by Logan Wright on 9/19/15. +// Copyright © 2015 lowriDevs. All rights reserved. +// + +import XCTest +@testable import PathIndexable + +enum Node { + case null + case bool(Bool) + case number(Double) + case string(String) + case array([Node]) + case object([String:Node]) +} + +extension Node: PathIndexable { + var pathIndexableArray: [Node]? { + guard case let .array(arr) = self else { + return nil + } + return arr + } + + var pathIndexableObject: [String: Node]? { + guard case let .object(ob) = self else { + return nil + } + return ob + } + + init(_ array: [Node]) { + self = .array(array) + } + + init(_ object: [String: Node]) { + self = .object(object) + } +} + +class PathIndexableTests: XCTestCase { + static var allTests = [ + ("testInt", testInt), + ("testString", testString), + ("testStringSequenceObject", testStringSequenceObject), + ("testStringSequenceArray", testStringSequenceArray), + ("testIntSequence", testIntSequence), + ("testMixed", testMixed), + ("testAccessNil", testAccessNil), + ] + + func testInt() { + let array: Node = .array(["one", + "two", + "three"].map(Node.string)) + guard let node = array[1] else { + XCTFail() + return + } + guard case let .string(val) = node else { + XCTFail() + return + } + + XCTAssert(val == "two") + } + + func testString() { + let object = Node(["a" : .number(1)]) + guard let node = object["a"] else { + XCTFail() + return + } + guard case let .number(val) = node else { + XCTFail() + return + } + + XCTAssert(val == 1) + } + + func testStringSequenceObject() { + let sub = Node(["path" : .string("found me!")]) + let ob = Node(["key" : sub]) + guard let node = ob["key", "path"] else { + XCTFail() + return + } + guard case let .string(val) = node else { + XCTFail() + return + } + + XCTAssert(val == "found me!") + } + + func testStringSequenceArray() { + let zero = Node(["a" : .number(0)]) + let one = Node(["a" : .number(1)]) + let two = Node(["a" : .number(2)]) + let three = Node(["a" : .number(3)]) + let obArray = Node([zero, one, two, three]) + + guard let collection = obArray["a"] else { + XCTFail() + return + } + guard case let .array(value) = collection else { + XCTFail() + return + } + + let mapped: [Double] = value.flatMap { node in + guard case let .number(val) = node else { + return nil + } + return val + } + XCTAssert(mapped == [0,1,2,3]) + } + + func testIntSequence() { + let inner = Node([.string("..."), + .string("found me!")]) + let outer = Node([inner]) + + guard let node = outer[0, 1] else { + XCTFail() + return + } + guard case let .string(value) = node else { + XCTFail() + return + } + + XCTAssert(value == "found me!") + } + + func testMixed() { + let array = Node([.string("a"), .string("b"), .string("c")]) + let mixed = Node(["one" : array]) + + guard let node = mixed["one", 1] else { + XCTFail() + return + } + guard case let .string(value) = node else { + XCTFail() + return + } + + XCTAssert(value == "b") + } + + func testOutOfBounds() { + var array = Node([.number(1.0), .number(2.0), .number(3.0)]) + XCTAssertNil(array[3]) + array[3] = .number(4.0) + XCTAssertNil(array[3]) + } + + func testSetArray() { + var array = Node([.number(1.0), .number(2.0), .number(3.0)]) + XCTAssertEqual(array[1], .number(2.0)) + array[1] = .number(4.0) + XCTAssertEqual(array[1], .number(4.0)) + array[1] = nil + XCTAssertEqual(array[1], .number(3.0)) + } + + func testMakeEmpty() { + let int: Int = 5 + let node: Node = int.makeEmptyStructure() + XCTAssertEqual(node, .array([])) + } + + func testAccessNil() { + let array = Node([.object(["test": .number(42)]), .number(5)]) + XCTAssertNil(array["foo"]) + + if let keyValResult = array["test"], case let .array(array) = keyValResult { + XCTAssertEqual(array.count, 1) + XCTAssertEqual(array.first, .number(42)) + } else { + XCTFail("Expected array result from array key val") + } + + let number = Node.number(5) + XCTAssertNil(number["test"]) + } + + func testSetObject() { + var object = Node([ + "one": .number(1.0), + "two": .number(2.0), + "three": .number(3.0) + ]) + XCTAssertEqual(object["two"], .number(2.0)) + object["two"] = .number(4.0) + XCTAssertEqual(object["two"], .number(4.0)) + object["two"] = nil + XCTAssertEqual(object["two"], nil) + + var array = Node([object, object]) + array["two"] = .number(5.0) + } + + func testPath() { + var object = Node([ + "one": Node([ + "two": .number(42) + ]) + ]) + XCTAssertEqual(object[path: "one.two"], .number(42)) + + object[path: "one.two"] = .number(5) + XCTAssertEqual(object[path: "one.two"], .number(5)) + + let comps = "one.two.5.&".keyPathComponents() + XCTAssertEqual(comps, ["one", "two", "5", "&"]) + } + + func testStringPathIndex() { + let path = ["hello", "3"] + let node = Node( + [ + "hello": .array([ + .string("a"), + .string("b"), + .string("c"), + .string("d") + ]) + ] + ) + + + if let n = node[path], case let .string(result) = n { + print(result) + XCTAssert(result == "d") + } else { + XCTFail("Expected result") + } + } +} + +extension Node: Equatable { + +} + +func ==(lhs: Node, rhs: Node) -> Bool { + switch (lhs, rhs) { + case (.number(let l), .number(let r)): + return l == r + case (.array(let l), .array(let r)): + return l == r + default: + return false + } +} diff --git a/Packages/PathIndexable-1.0.0/circle.yml b/Packages/PathIndexable-1.0.0/circle.yml new file mode 100644 index 0000000..edd2fba --- /dev/null +++ b/Packages/PathIndexable-1.0.0/circle.yml @@ -0,0 +1,3 @@ +test: + override: + - eval "$(curl -sL swift.vapor.sh/ci)" diff --git a/Packages/Polymorphic-1.0.0/.gitignore b/Packages/Polymorphic-1.0.0/.gitignore new file mode 100644 index 0000000..bbaa920 --- /dev/null +++ b/Packages/Polymorphic-1.0.0/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +.build +*.xcodeproj +Packages diff --git a/Packages/Polymorphic-1.0.0/.travis.yml b/Packages/Polymorphic-1.0.0/.travis.yml new file mode 100644 index 0000000..6f74cb2 --- /dev/null +++ b/Packages/Polymorphic-1.0.0/.travis.yml @@ -0,0 +1,10 @@ +os: + - linux + - osx +language: generic +sudo: required +dist: trusty +osx_image: xcode8 +script: + - eval "$(curl -sL swift.vapor.sh/ci)" + - eval "$(curl -sL swift.vapor.sh/codecov)" diff --git a/Packages/Polymorphic-1.0.0/LICENSE b/Packages/Polymorphic-1.0.0/LICENSE new file mode 100644 index 0000000..0201c63 --- /dev/null +++ b/Packages/Polymorphic-1.0.0/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Qutheory + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Packages/Polymorphic-1.0.0/Package.swift b/Packages/Polymorphic-1.0.0/Package.swift new file mode 100644 index 0000000..ed76df5 --- /dev/null +++ b/Packages/Polymorphic-1.0.0/Package.swift @@ -0,0 +1,5 @@ +import PackageDescription + +let package = Package( + name: "Polymorphic" +) diff --git a/Packages/Polymorphic-1.0.0/README.md b/Packages/Polymorphic-1.0.0/README.md new file mode 100644 index 0000000..7c437cd --- /dev/null +++ b/Packages/Polymorphic-1.0.0/README.md @@ -0,0 +1,22 @@ +# Polymorphic + +![Swift](http://img.shields.io/badge/swift-3.0-brightgreen.svg) +[![Build Status](https://travis-ci.org/vapor/polymorphic.svg?branch=master)](https://travis-ci.org/vapor/polymorphic) +[![CircleCI](https://circleci.com/gh/vapor/polymorphic.svg?style=shield)](https://circleci.com/gh/vapor/polymorphic) +[![Code Coverage](https://codecov.io/gh/vapor/polymorphic/branch/master/graph/badge.svg)](https://codecov.io/gh/vapor/polymorphic) +[![Codebeat](https://codebeat.co/badges/a793ad97-47e3-40d9-82cf-2aafc516ef4e)](https://codebeat.co/projects/github-com-vapor-polymorphic) +[![Slack Status](http://vapor.team/badge.svg)](http://vapor.team) + +Syntax for easily accessing values from generic data. + +## 📖 Documentation + +Visit the Vapor web framework's [documentation](http://docs.vapor.codes) for instructions on how to use this package. + +## 💧 Community + +Join the welcoming community of fellow Vapor developers in [slack](http://vapor.team). + +## 🔧 Compatibility + +This library has been tested on macOS and Ubuntu. diff --git a/Packages/Polymorphic-1.0.0/Sources/Polymorphic.swift b/Packages/Polymorphic-1.0.0/Sources/Polymorphic.swift new file mode 100644 index 0000000..af1f547 --- /dev/null +++ b/Packages/Polymorphic-1.0.0/Sources/Polymorphic.swift @@ -0,0 +1,34 @@ +/** + This protocol provides syntax for + easily accessing values from generic data. +*/ +public protocol Polymorphic { + // Required + var isNull: Bool { get } + var bool: Bool? { get } + var double: Double? { get } + var int: Int? { get } + var string: String? { get } + var array: [Polymorphic]? { get } + var object: [String : Polymorphic]? { get } + + // Optional + var float: Float? { get } + var uint: UInt? { get } +} + +extension Polymorphic { + public var float: Float? { + guard let double = double else { + return nil + } + + return Float(double) + } + + public var uint: UInt? { + guard let i = int else { return nil } + guard i >= 0 else { return nil } + return UInt(i) + } +} diff --git a/Packages/Polymorphic-1.0.0/Sources/String+Polymorphic.swift b/Packages/Polymorphic-1.0.0/Sources/String+Polymorphic.swift new file mode 100644 index 0000000..b442528 --- /dev/null +++ b/Packages/Polymorphic-1.0.0/Sources/String+Polymorphic.swift @@ -0,0 +1,114 @@ +extension String: Polymorphic { + /** + Determines whether or not the `String` is null. + Returns `true` if the `String` is equal to `"null"`. + */ + public var isNull: Bool { + return self.lowercased() == "null" + } + + /** + Attempts to convert the String to a `Bool`. + The conversion **may** succeed if the `String` + has a truthy/falsey value like `"yes"` or `"false"` + + All others will always return `nil`. + */ + public var bool: Bool? { + switch lowercased() { + case "y", "1", "yes", "t", "true": + return true + case "n", "0", "no", "f", "false": + return false + default: + return nil + } + } + + /** + Attempts to convert the `String` to a `Float`. + The conversion uses the `Float(_: String)` initializer. + */ + public var float: Float? { + return Float(self) + } + + /** + Attempts to convert the `String` to a `Double`. + The conversion uses the `Double(_: String)` initializer. + */ + public var double: Double? { + return Double(self) + } + + /** + Attempts to convert the `String` to a `Int`. + The conversion uses the `Int(_: String)` initializer. + */ + public var int: Int? { + return Int(self) + } + + /** + Attempts to convert the `String` to a `UInt`. + The conversion uses the `UInt(_: String)` initializer. + */ + public var uint: UInt? { + return UInt(self) + } + + /** + Attempts to convert the `String` to a `String`. + This always works. + */ + public var string: String? { + return self + } + + /** + Attempts to convert the `String` to an `Array`. + Comma separated items will be split into + multiple entries. + */ + public var array: [Polymorphic]? { + return characters + .split(separator: ",") + .map { String($0) } + .map { $0.trimmedWhitespace() } + .map { $0 as Polymorphic } + } + + /** + Attempts to convert the `String` to a `Dictionary`. + This conversion always fails. + */ + public var object: [String : Polymorphic]? { + return nil + } +} + +extension String { + func trimmedWhitespace() -> String { + var characters = self.characters + + while characters.first?.isWhitespace == true { + characters.removeFirst() + } + while characters.last?.isWhitespace == true { + characters.removeLast() + } + + return String(characters) + } +} + +extension Character { + var isWhitespace: Bool { + switch self { + case " ", "\t", "\n", "\r": + return true + default: + return false + } + } +} diff --git a/Packages/Polymorphic-1.0.0/Tests/LinuxMain.swift b/Packages/Polymorphic-1.0.0/Tests/LinuxMain.swift new file mode 100644 index 0000000..b8941ea --- /dev/null +++ b/Packages/Polymorphic-1.0.0/Tests/LinuxMain.swift @@ -0,0 +1,10 @@ +#if os(Linux) + + import XCTest + @testable import PolymorphicTests + +XCTMain([ + testCase(PolymorphicTests.allTests) +]) + +#endif diff --git a/Packages/Polymorphic-1.0.0/Tests/PolymorphicTests/PolymorphicTests.swift b/Packages/Polymorphic-1.0.0/Tests/PolymorphicTests/PolymorphicTests.swift new file mode 100644 index 0000000..2b62884 --- /dev/null +++ b/Packages/Polymorphic-1.0.0/Tests/PolymorphicTests/PolymorphicTests.swift @@ -0,0 +1,101 @@ +import XCTest +@testable import Polymorphic + +class PolymorphicTests: XCTestCase { + static var allTests = [ + ("testInt", testInt), + ("testUInt", testUInt), + ("testArray", testArray), + ("testObject", testObject), + ("testFloat", testFloat), + ("testDouble", testDouble), + ("testNull", testNull), + ("testBool", testBool), + ("testDefaults", testDefaults), + ] + + func testInt() { + let poly = "-123" + XCTAssert(poly.int == -123) + XCTAssert(poly.uint == nil) + XCTAssert(poly.string == "-123") + } + + func testUInt() { + let poly = UInt.max.description + XCTAssert(poly.uint == UInt.max) + XCTAssert(poly.int == nil) + } + + func testArray() { + let list = "oranges, apples , bananas, grapes" + print(list.array) + let fruits = list.array?.flatMap { $0.string } ?? [] + XCTAssert(fruits == ["oranges", "apples", "bananas", "grapes"]) + } + + func testObject() { + let obstr = "***" + XCTAssert(obstr.object == nil) + } + + func testFloat() { + let poly = "3.14159" + XCTAssert(poly.float == 3.14159) + } + + func testDouble() { + let poly = "999999.999" + XCTAssert(poly.double == 999_999.999) + } + + func testNull() { + XCTAssert("null".isNull == true) + XCTAssert("NULL".isNull == true) + } + + func testBool() { + XCTAssert("y".bool == true) + XCTAssert("yes".bool == true) + XCTAssert("t".bool == true) + XCTAssert("true".bool == true) + XCTAssert("1".bool == true) + + + XCTAssert("n".bool == false) + XCTAssert("no".bool == false) + XCTAssert("f".bool == false) + XCTAssert("false".bool == false) + XCTAssert("0".bool == false) + + XCTAssert("nothing".bool == nil) + XCTAssert("to".bool == nil) + XCTAssert("see".bool == nil) + XCTAssert("here".bool == nil) + } + + func testDefaults() { + struct Test: Polymorphic { + var int: Int? + var double: Double? + + var isNull: Bool { return false } + var bool: Bool? { return nil } + var string: String? { return nil } + var array: [Polymorphic]? { return nil } + var object: [String : Polymorphic]? { return nil } + } + + var a = Test(int: 42, double: 3.14159) + XCTAssertEqual(a.uint, 42) + XCTAssertEqual(a.float, 3.14159) + a.double = nil + XCTAssertEqual(a.float, nil) + + let b = Test(int: nil, double: nil) + XCTAssert(b.uint == nil) + + let c = Test(int: -123, double: nil) + XCTAssert(c.uint == nil) + } +} diff --git a/Packages/Polymorphic-1.0.0/circle.yml b/Packages/Polymorphic-1.0.0/circle.yml new file mode 100644 index 0000000..edd2fba --- /dev/null +++ b/Packages/Polymorphic-1.0.0/circle.yml @@ -0,0 +1,3 @@ +test: + override: + - eval "$(curl -sL swift.vapor.sh/ci)"