Created
July 10, 2019 00:16
-
-
Save d-srd/c633c5d36af84faa6716e8260ac49188 to your computer and use it in GitHub Desktop.
testing the new Swift function builders for creating a simple routing system
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// RouteBuilder.swift | |
// RouteBuilder | |
// | |
// Created by Dino on 09/07/2019. | |
// Copyright © 2019 Dino Srdoc. All rights reserved. | |
// | |
import Foundation | |
struct Router { | |
let routes: [Route] | |
init(@RouterBuilder _ content: () -> Router) { | |
self = content() | |
} | |
init(routes: [Route]) { | |
self.routes = routes | |
} | |
func handle(request: URLRequest) -> Response { | |
guard let responder = routes.first(where: { $0.responds(to: request)} ) else { | |
return Response(statusCode: 500, message: "Unable to handle request") | |
} | |
return responder.handler.handle(request) | |
} | |
} | |
@_functionBuilder | |
class RouterBuilder { | |
static func buildBlock(_ children: Route...) -> Router { | |
Router(routes: children) | |
} | |
static func buildFunction(_ children: Route...) -> Router { | |
Router(routes: children) | |
} | |
} | |
struct Route { | |
let destination: String | |
let method: Method | |
let handler: Handler | |
init(@RouteBuilder _ content: () -> Route) { | |
self = content() | |
} | |
init(destination: String, method: Method, handler: Handler) { | |
self.destination = destination | |
self.method = method | |
self.handler = handler | |
} | |
func responds(to request: URLRequest) -> Bool { | |
guard let url = request.url, | |
let components = URLComponents(url: url, resolvingAgainstBaseURL: true) | |
else { return false } | |
return components.path == destination && request.httpMethod == method.spelledOut | |
} | |
} | |
protocol DescribingRoute { } | |
enum Method: DescribingRoute { | |
case put | |
case post | |
case get | |
var spelledOut: String { | |
switch self { | |
case .put: | |
return "PUT" | |
case .post: | |
return "POST" | |
case .get: | |
return "GET" | |
} | |
} | |
} | |
let PUT = Method.put | |
let POST = Method.post | |
let GET = Method.get | |
struct Destination: DescribingRoute { | |
let destination: String | |
init(_ destination: String) { | |
self.destination = destination | |
} | |
} | |
extension String: DescribingRoute { } | |
struct Response { | |
let statusCode: Int | |
let message: String | |
} | |
struct Handler: DescribingRoute { | |
let handle: (URLRequest) -> Response | |
} | |
struct Identified: DescribingRoute { | |
let destinationURL: String | |
init(_ destination: String) { | |
self.destinationURL = destination | |
} | |
} | |
@_functionBuilder | |
class RouteBuilder { | |
static func buildBlock(_ children: DescribingRoute...) -> Route { | |
let method = children.first(where: { $0 is Method }) as! Method | |
let handler = children.first(where: { $0 is Handler }) as! Handler | |
if let identified = children.first(where: { $0 is Identified }) as? Identified { | |
return Route(destination: identified.destinationURL, method: method, handler: handler) | |
} | |
if let destination = children.first(where: { $0 is String }) as? String { | |
return Route(destination: destination, method: method, handler: handler) | |
} | |
fatalError("Must provide a destination for the Route") | |
} | |
} | |
let router = Router { | |
Route { | |
GET | |
"/api/user" | |
Handler { request in | |
Response(statusCode: 200, message: "Here are some users") | |
} | |
} | |
Route { | |
POST | |
"/api/user" | |
Handler { request in | |
Response(statusCode: 200, message: "Did you mean to post some stuff?") | |
} | |
} | |
Route { | |
GET | |
Identified("/api/user/%S") | |
Handler { request in | |
Response(statusCode: 500, message: "Fatal error") | |
} | |
} | |
} | |
func doStuff() { | |
var req0 = URLRequest(url: URL(string: "https://www.mojsuperapi.com/api/user")!) | |
req0.httpMethod = GET.spelledOut | |
let resp0 = router.handle(request: req0) | |
dump(resp0) | |
// ▿ RouteBuilder.Response | |
// - statusCode: 200 | |
// - message: "Here are some users" | |
var req1 = URLRequest(url: URL(string: "https://www.mojsuperapi.com/api/user")!) | |
req1.httpMethod = POST.spelledOut | |
let resp1 = router.handle(request: req1) | |
dump(resp1) | |
// ▿ RouteBuilder.Response | |
// - statusCode: 200 | |
// - message: "Did you mean to post some stuff?" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment