Skip to content

Instantly share code, notes, and snippets.

@chriseidhof chriseidhof/routing.swift
Last active Aug 24, 2019

Embed
What would you like to do?
routing_attempt_n
import Foundation
struct Episode: Codable { }
struct EpisodeDetail: Codable { }
// episodes.json returns an [Episode]
// /episodes/foo should be (String) -> EpisodeDetail
enum Either<A, B> {
case left(A)
case right(B)
}
struct Request {
var urlParts: [String] = []
var data: Data = Data()
func prepending(_ str: String) -> Request {
var copy = self
copy.urlParts = [str] + copy.urlParts
return copy
}
}
extension Request: CustomStringConvertible {
var description: String {
return "Request { \(urlParts.joined(separator: "/")) \(String(data: data, encoding: .utf8) ?? data.description) }"
}
}
protocol RoutePart {
associatedtype ParseResult
associatedtype URLParams
func request(_ params: URLParams) -> Request
}
struct Constant<A: RoutePart>: RoutePart {
typealias ParseResult = A.ParseResult
typealias URLParams = A.URLParams
var name: String
var body: A
func request(_ params: Constant<A>.URLParams) -> Request {
return body.request(params).prepending(name)
}
}
struct StringParam<A: RoutePart>: RoutePart {
typealias ParseResult = (String, A.ParseResult)
typealias URLParams = (String, A.URLParams)
let nested: A
init(_ nested: A) {
self.nested = nested
}
func request(_ params: (String, A.URLParams)) -> Request {
return nested.request(params.1).prepending(params.0)
}
}
struct Choice<A: RoutePart, B: RoutePart>: RoutePart {
typealias ParseResult = Either<A.ParseResult, B.ParseResult>
typealias URLParams = Either<A.URLParams, B.URLParams>
let lhs: A
let rhs: B
func request(_ params: Either<A.URLParams, B.URLParams>) -> Request {
switch params {
case let .left(l): return lhs.request(l)
case let .right(r): return rhs.request(r)
}
}
}
func /<A: RoutePart>(lhs: String, rhs: A) -> Constant<A> {
return Constant(name: lhs, body: rhs)
}
struct Empty: Codable { }
struct JSON<PostBody: Codable, Response: Codable>: RoutePart {
typealias ParseResult = Response?
func request(_ params: PostBody) -> Request {
let encoder = JSONEncoder()
return Request(urlParts: [], data: try! encoder.encode(params))
}
}
struct HTML: RoutePart {
typealias ParseResult = ()
func request(_ params: ()) -> Request {
return Request(urlParts: [], data: Data())
}
}
extension RoutePart {
func or<B: RoutePart>(_ rhs: B) -> Choice<Self, B> {
return Choice(lhs: self, rhs: rhs)
}
}
protocol NamedRoute {
associatedtype R: RoutePart
static var route: R { get }
}
enum Episodes: NamedRoute {
struct Detail: NamedRoute {
static let show_ = JSON<Empty, EpisodeDetail>()
static let update_ = "update" / JSON<EpisodeDetail, Bool>()
static let route = show_.or(update_)
static let show: R.URLParams = .left(Empty())
static let update: (EpisodeDetail) -> R.URLParams = { .right($0) }
}
static let all_ = JSON<Empty, [Episode]>()
static let create_ = JSON<Episode, Empty>()
static let detail_ = StringParam(Detail.route)
static let route = "episodes" / detail_.or(all_.or(create_))
static let detail: (String, Detail.R.URLParams) -> R.URLParams = { str, params in .left((str, params)) }
static let all: R.URLParams = .right(.left(Empty()))
static let create: (Episode) -> R.URLParams = { ep in .right(.right(ep)) }
}
let detailRout = Episodes.detail("hi", Episodes.Detail.show)
let allRoute = Episodes.all
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.