import Foundation protocol GQLNodeArgument {} extension String: GQLNodeArgument {} extension NSNumber: GQLNodeArgument {} class GQLNode: StringLiteralConvertible, ArrayLiteralConvertible, Printable, DebugPrintable { let name: String? let arguments: [String : GQLNodeArgument] let properties: [GQLNode] var isRootNode: Bool { return self.name?.isEmpty ?? true } init(name: String, arguments: [String : GQLNodeArgument] = [:], properties: [GQLNode] = []) { for node in properties { assert(!node.isRootNode, "You cannot include a root node, or a node with an empty name as a child of another node.") } self.name = name self.arguments = arguments self.properties = properties } required convenience init(stringLiteral value: StringLiteralType) { self.init(name: value) } required convenience init(extendedGraphemeClusterLiteral value: StringLiteralType) { self.init(name: value) } required convenience init(unicodeScalarLiteral value: StringLiteralType) { self.init(name: value) } required convenience init(arrayLiteral elements: GQLNode...) { self.init(name: "", properties: elements) } func render(pretty: Bool = false, indentationLevel: Int = 0) -> String { var rendered: String = "" let indentationText: String if pretty { indentationText = "".join(Array(count: indentationLevel, repeatedValue: " ")) } else { indentationText = "" } rendered += indentationText if let name = self.name { rendered += name } if self.arguments.count > 0 { rendered += "(" var argumentTexts: [String] = [] for (name, argument) in self.arguments { argumentTexts.append("\(name): \(argument)") } rendered += ", ".join(argumentTexts) if pretty { rendered += ") " } else { rendered += ")" } } if self.properties.count > 0 { rendered += "{" if pretty { rendered += "\n" } var propertyTexts: [String] = [] for property in self.properties { propertyTexts.append(property.render(pretty: pretty, indentationLevel: indentationLevel + 1)) } if pretty { rendered += ",\n".join(propertyTexts) } else { rendered += ",".join(propertyTexts) } if pretty { rendered += "\n" } rendered += indentationText + "}" } return rendered } var description: String { return self.render(pretty: true) } var debugDescription: String { return "GQLNode(\(self.render(pretty: true)))" } } let query: GQLNode = [ GQLNode(name: "user", arguments: ["id" : 3500401], properties: [ "id", "name", "isViewerFriend", GQLNode(name: "profilePicture", arguments: ["size" : 50], properties: [ "uri", "width", "height", ]), ]) ] query.render(pretty: true) query.render(pretty: false)