<commit_before>//
//  TypedRoutes.swift
//  PerfectHTTP
//
//  Created by <NAME> on 2017-12-18.
//

import Foundation
import PerfectThread

extension HTTPResponseStatus: Codable {
	/// Codable support for HTTPResponseStatus
	public init(from decoder: Decoder) throws {
		self = HTTPResponseStatus.statusFrom(code: try Int(from: decoder))
	}
	/// Codable support for HTTPResponseStatus
	public func encode(to encoder: Encoder) throws {
		try code.encode(to: encoder)
	}
}

/// A codable response type indicating an error.
public struct HTTPResponseError: Error, Codable {
	/// The HTTP status for the response.
	public let status: HTTPResponseStatus
	/// Textual description of the error.
	public let description: String
	/// Init with status and description.
	public init(status s: HTTPResponseStatus,
				description d: String) {
		status = s
		description = d
	}
}

public protocol HTTPResponseContentProvider {
	func provide(response: HTTPResponse) throws -> HTTPResponse
}

/// Return content which has a chance to modify the response, set status or add headers.
public struct HTTPResponseContent<BodyType: Codable>: HTTPResponseContentProvider {
	public let status: HTTPResponseStatus
	public let responseHeaders: [(HTTPResponseHeader.Name, String)]
	public let body: BodyType
	public let finalFilter: ((HTTPResponse) -> ())?
	public init(status: HTTPResponseStatus = .ok,
				responseHeaders: [(HTTPResponseHeader.Name, String)] = [],
				body: BodyType,
				finalFilter: ((HTTPResponse) -> ())? = nil) {
		self.status = status
		self.responseHeaders = responseHeaders
		self.body = body
		self.finalFilter = finalFilter
	}
	public func provide(response: HTTPResponse) throws -> HTTPResponse {
		response.status = status
		try response.encode(body)
		responseHeaders.forEach { response.setHeader($0.0, value: $0.1) }
		finalFilter?(response)
		return response
	}
}

/// Return no content with a chance to modify the response, set status or add headers.
public struct HTTPResponseNoContent: HTTPResponseContentProvider {
	public let status: HTTPResponseStatus
	public let responseHeaders: [(HTTPResponseHeader.Name, String)]
	public let finalFilter: ((HTTPResponse) -> ())?
	public init(status: HTTPResponseStatus = .ok,
				responseHeaders: [(HTTPResponseHeader.Name, String)] = [],
				finalFilter: ((HTTPResponse) -> ())? = nil) {
		self.status = status
		self.responseHeaders = responseHeaders
		self.finalFilter = finalFilter
	}
	public func provide(response: HTTPResponse) throws -> HTTPResponse {
		response.status = status
		responseHeaders.forEach { response.setHeader($0.0, value: $0.1) }
		finalFilter?(response)
		return response
	}
}

private let lastObjectKey = "_last_object_"

/// Extensions on HTTPRequest which permit the request body to be decoded to a Codable type.
public extension HTTPRequest {
	/// Decode the request body into the desired type, or throw an error.
	func decode<A: Codable>(_ type: A.Type) throws -> A {
		return try decode()
	}
	/// Decode the request body into the desired type, or throw an error.
	func decode<A: Codable>() throws -> A {
		if let contentType = header(.contentType), contentType.hasPrefix("application/json") {
			guard let body = postBodyBytes else {
				throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "This request requires JSON input."))
			}
			// this is not exactly ideal
			// fudging url vars and json post body is inefficient
			let data: Data
			if !urlVariables.isEmpty,
				var dict = try JSONSerialization.jsonObject(with: Data(bytes: body), options: []) as? [String:Any] {
				urlVariables.forEach {
					let (key, value) = $0
					dict[key] = value
				}<commit_msg>Fixed type constraint for decode func<commit_after>//
//  TypedRoutes.swift
//  PerfectHTTP
//
//  Created by <NAME> on 2017-12-18.
//

import Foundation
import PerfectThread

extension HTTPResponseStatus: Codable {
	/// Codable support for HTTPResponseStatus
	public init(from decoder: Decoder) throws {
		self = HTTPResponseStatus.statusFrom(code: try Int(from: decoder))
	}
	/// Codable support for HTTPResponseStatus
	public func encode(to encoder: Encoder) throws {
		try code.encode(to: encoder)
	}
}

/// A codable response type indicating an error.
public struct HTTPResponseError: Error, Codable {
	/// The HTTP status for the response.
	public let status: HTTPResponseStatus
	/// Textual description of the error.
	public let description: String
	/// Init with status and description.
	public init(status s: HTTPResponseStatus,
				description d: String) {
		status = s
		description = d
	}
}

public protocol HTTPResponseContentProvider {
	func provide(response: HTTPResponse) throws -> HTTPResponse
}

/// Return content which has a chance to modify the response, set status or add headers.
public struct HTTPResponseContent<BodyType: Codable>: HTTPResponseContentProvider {
	public let status: HTTPResponseStatus
	public let responseHeaders: [(HTTPResponseHeader.Name, String)]
	public let body: BodyType
	public let finalFilter: ((HTTPResponse) -> ())?
	public init(status: HTTPResponseStatus = .ok,
				responseHeaders: [(HTTPResponseHeader.Name, String)] = [],
				body: BodyType,
				finalFilter: ((HTTPResponse) -> ())? = nil) {
		self.status = status
		self.responseHeaders = responseHeaders
		self.body = body
		self.finalFilter = finalFilter
	}
	public func provide(response: HTTPResponse) throws -> HTTPResponse {
		response.status = status
		try response.encode(body)
		responseHeaders.forEach { response.setHeader($0.0, value: $0.1) }
		finalFilter?(response)
		return response
	}
}

/// Return no content with a chance to modify the response, set status or add headers.
public struct HTTPResponseNoContent: HTTPResponseContentProvider {
	public let status: HTTPResponseStatus
	public let responseHeaders: [(HTTPResponseHeader.Name, String)]
	public let finalFilter: ((HTTPResponse) -> ())?
	public init(status: HTTPResponseStatus = .ok,
				responseHeaders: [(HTTPResponseHeader.Name, String)] = [],
				finalFilter: ((HTTPResponse) -> ())? = nil) {
		self.status = status
		self.responseHeaders = responseHeaders
		self.finalFilter = finalFilter
	}
	public func provide(response: HTTPResponse) throws -> HTTPResponse {
		response.status = status
		responseHeaders.forEach { response.setHeader($0.0, value: $0.1) }
		finalFilter?(response)
		return response
	}
}

private let lastObjectKey = "_last_object_"

/// Extensions on HTTPRequest which permit the request body to be decoded to a Codable type.
public extension HTTPRequest {
	/// Decode the request body into the desired type, or throw an error.
	func decode<A: Decodable>(_ type: A.Type) throws -> A {
		return try decode()
	}
	/// Decode the request body into the desired type, or throw an error.
	func decode<A: Decodable>() throws -> A {
		if let contentType = header(.contentType), contentType.hasPrefix("application/json") {
			guard let body = postBodyBytes else {
				throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "This request requires JSON input."))
			}
			// this is not exactly ideal
			// fudging url vars and json post body is inefficient
			let data: Data
			if !urlVariables.isEmpty,
				var dict = try JSONSerialization.jsonObject(with: Data(bytes: body), options: []) as? [String:Any] {
				urlVariables.forEach {
					let (key, value) = $0
					dict[key] = value
				}
{'fraction_non_alphanumeric': 0.10230664857530529, 'fraction_numerical': 0.004613297150610584, 'mean_word_length': 4.1835443037974684, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3295387', 'n_tokens_mistral': 2242, 'n_tokens_neox': 2040, 'n_words': 701}