Created
August 24, 2015 12:04
-
-
Save chriseidhof/625eeba5f568eb222149 to your computer and use it in GitHub Desktop.
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
enum Request { | |
case ProductsList // "/products" | |
case Product(productID: Int) // "/products/:pid" | |
case CustomersList // "/customers" | |
case Customer(customerID: Int) // "/customers/:cid" | |
case OrdersList(customerID: Int) // "/customers/:cid/orders" | |
case Order(customerID: Int, orderID: Int) // "/customers/:cid/orders/:oid" | |
} | |
extension Array { | |
var decompose: (Element, [Element])? { | |
return isEmpty ? nil : (self[startIndex], Array(self.dropFirst())) | |
} | |
} | |
func curry<A, B, C>(f: (A, B) -> C) -> A -> B -> C { | |
return { x in { y in f(x, y) } } | |
} | |
func curry<A, B, C, D>(f: (A, B, C) -> D)(_ x: A)(_ y: B)(_ z: C) -> D { | |
return f(x, y, z) | |
} | |
struct Match<A> { | |
let match: [String] -> (A, [String])? | |
init(raw x: [String] -> (A, [String])?) { | |
match = x | |
} | |
init(pure x: A) { | |
match = { (x, $0) } | |
} | |
} | |
extension Match { | |
func parse(components: [String]) -> A? { | |
guard case let (x, result)? = match(components) where result.isEmpty else { return nil } | |
return x | |
} | |
} | |
let int: Match<Int> = Match { | |
if let (x, rest) = $0.decompose, | |
let result = Int(x) { | |
return (result, rest) | |
} | |
return nil | |
} | |
/// Matches a constant string | |
func c(string: String) -> Match<String> { | |
return Match { | |
guard $0.first == string else { return nil } | |
return $0.decompose | |
} | |
} | |
/// Choice | |
infix operator <|> { associativity left precedence 130 } | |
func <|><A>(lhs: Match<A>, rhs: Match<A>) -> Match<A> { | |
return Match { | |
return lhs.match($0) ?? rhs.match($0) | |
} | |
} | |
infix operator <*> { associativity left precedence 150 } | |
func <*><A, B>(lhs: Match<A -> B>, rhs: Match<A>) -> Match<B> { | |
return Match { elements in | |
guard let (x, elements1) = lhs.match(elements) else { return nil } | |
guard let (y, elements2) = rhs.match(elements1) else { return nil } | |
return (x(y), elements2) | |
} | |
} | |
infix operator <* { associativity left precedence 150 } | |
func <*<A, B>(lhs: Match<A>, rhs: Match<B>) -> Match<A> { | |
return Match { elements in | |
guard let (x, newElements) = lhs.match(elements) else { return nil } | |
guard let (_, newerElements) = rhs.match(newElements) else { return nil } | |
return (x, newerElements) | |
} | |
} | |
func pure<A>(x: A) -> Match<A> { return Match { (x, $0) } } | |
let route = | |
Match(pure: Request.Product) <* c("products") <*> int <|> | |
Match(pure: Request.ProductsList) <* c("products") <|> | |
Match(pure: curry(Request.Order)) <* c("customers") <*> int <* c("orders") <*> int <|> | |
Match(pure: Request.Customer) <* c("customers") <*> int <|> | |
Match(pure: Request.CustomersList) <* c("customers") | |
let result = route.parse(["customers"]) | |
print(result) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment