Last active
August 24, 2019 09:32
-
-
Save chriseidhof/561266129bbbedb3cb1b774071218771 to your computer and use it in GitHub Desktop.
routing_attempt_n
This file contains 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
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