Unlock This Episode
Our Free plan includes 1 subscriber-only episode of your choice, plus weekly updates from our newsletter.
Introduction
Now although it is a little annoying that we have to sprinkle a little bit of extra syntax to let the compiler know exactly what type it is working with, it is also forcing a very good pattern on us. The prefix
parser only works for collections whose SubSequence
is equal to itself. Such collections are able to mutate themselves in an efficient manner because they operate on views of a shared storage rather than make needless copies all over the place.
This is why we are forced to provide array slices and substrings as input to these parsers, because it allows them to do their work in the most efficient way possible.
Subscribe to Point-Free
Access this episode, plus all past and future episodes when you become a subscriber.
Already a subscriber? Log in
Exercises
Write a function that transforms a
URLRequest
into the parser-friendlyRequestData
type.Solution
extension RequestData { init(urlRequest: URLRequest) { self.body = urlRequest.httpBody self.headers = urlRequest.allHTTPHeaderFields?.mapValues { $0[...] } ?? [:] self.method = urlRequest.httpMethod self.pathComponents = urlRequest.url?.path.split(separator: "/")[...] ?? [] self.queryItems = urlRequest.url .flatMap { URLComponents(url: $0, resolvingAgainstBaseURL: false)?.queryItems? .map { ($0.name, $0.value?[...] ?? "") } } ?? [] } }
Implement a header parser that can parse a value from a given header name. For example:
Parser.header(name: "Content-Length", .int) // Parser<RequestData, Int>
Solution
extension Parser where Input == RequestData { static func header(name: String, _ parser: Parser<Substring, Output>) -> Self { .init { input in guard var value = input.headers[name], let output = parser.run(&value), value.isEmpty else { return nil } input.headers[name] = nil return output } } }
Implement a JSON body parser that can decode a particular value from a request body. For example:
struct User: Decodable { var id: Int var name: String } Parser.body(as: User.self, decoder: JSONDecoder()) // Parser<RequestData, User>
Solution
extension Parser where Input == RequestData { static func body( as decodable: Output.Type = Output.self, decoder: JSONDecoder = .init() ) -> Self where Output: Decodable { .init { input in guard let data = input.body, let output = try? decoder.decode(decodable, from: data) else { return nil } input.body = nil return output } } }