GraphQL data structure implemented in Swift

May 2, 2015 ยท View on GitHub

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)