<commit_before>//
//  Routing.swift
//  PerfectLib
//
//  Created by <NAME> on 2015-12-11.
//  Copyright © 2015 PerfectlySoft. All rights reserved.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//


/// Holds the registered routes.
public struct RouteMap: CustomStringConvertible {
	
	public typealias RequestHandler = (WebRequest, WebResponse) -> ()
	
	/// Pretty prints all route information.
	public var description: String {
		var s = self.root.description
		for (method, root) in self.methodRoots {
			s.append("\n" + method + ":\n" + root.description)
		}
		return s
	}
	
	private let root = RouteNode() // root node for any request method
	private var methodRoots = [String:RouteNode]() // by convention, use all upper cased method names for inserts/lookups
	
	// Lookup a route based on the URL path.
	// Returns the handler generator if found.
	subscript(path: String, webResponse: WebResponse) -> RequestHandler? {
		get {
			let components = path.lowercased().pathComponents
			var g = components.makeIterator()
			let _ = g.next() // "/"
			
			let method = webResponse.request.requestMethod!.uppercased()
			if let root = self.methodRoots[method] {
				if let handler = root.findHandler(currentComponent: "", generator: g, webResponse: webResponse) {
					return handler
				}
			}
			return self.root.findHandler(currentComponent: "", generator: g, webResponse: webResponse)
		}
	}
	
	/// Add a route to the system.
	/// `Routing.Routes["/foo/*/baz"] = { _ in return ExampleHandler() }`
	public subscript(path: String) -> RequestHandler? {
		get {
			return nil // Swift does not currently allow set-only subscripts
		}
		set {
			self.root.addPathSegments(generator: path.lowercased().pathComponents.makeIterator(), handler: newValue!)
		}
	}
	
	/// Add an array of routes for a given handler.
	/// `Routing.Routes[ ["/", "index.html"] ] = { _ in return ExampleHandler() }`
	public subscript(paths: [String]) -> RequestHandler? {
		get {
			return nil
		}
		set {
			for path in paths {
				self[path] = newValue
			}
		}
	}
	
	/// Add a route to the system using the indicated HTTP request method.
	/// `Routing.Routes["GET", "/foo/*/baz"] = { _ in return ExampleHandler() }`
	public subscript(method: String, path: String) -> RequestHandler? {
		get {
			return nil // Swift does not currently allow set-only subscripts
		}
		set {
			let uppered = method.uppercased()
			if let root = self.methodRoots[uppered] {
				root.addPathSegments(generator: path.lowercased().pathComponents.makeIterator(), handler: newValue!)
			} else {
				let root = RouteNode()
				self.methodRoots[uppered] = root
				root.addPathSegments(generator: path.lowercased().pathComponents.makeIterator(), handler: newValue!)
			}
		}
	}
	
	/// Add an array of routes for a given handler using the indicated HTTP request method.
	/// `Routing.Routes["GET", ["/", "index.html"] ] = { _ in return ExampleHandler() }`
	public subscript(method: String, paths: [String]) -> RequestHandler? {
		get {
			return nil // Swift does not currently allow set-only subscripts
		}
		set {
			for path in paths {
				self[method, path] = newValue
			}
		}
	}
}

/// This wraps up the routing related functionality.
/// Enable the routing system by calling:
/// ```
/// Routing.Handler.registerGlobally()
/// ```
/// This should be done in your `PerfectServerModuleInit` function.
/// The system supports HTTP method based routing, wildcards and variables.
///
/// Add routes in the following manner:
/// ```
/// 	Routing.Routes["GET", ["/", "index.html"] ] = { (_:WebResponse) in return IndexHandler() }
/// 	Routing.Routes["/foo/*/baz"] = { _ in return EchoHandler() }
/// 	Routing.Routes["/foo/bar/baz"] = { _ in return EchoHandler() }
/// 	Routing.Routes["GET", "/user/{id}/baz"] = { _ in return Echo2Handler() }
/// 	Routing.Routes["POST", "/user/{id}/baz"] = { _ in return Echo3Handler() }
/// ```
/// The closure you provide should return an instance of `PageHandler`. It is provided the WebResponse object to permit further customization.
/// Variables set by the routing process can be accessed through the `WebRequest.urlVariables` dictionary.
/// Note that a PageHandler *MUST* call `WebResponse.requestCompleted()` when the request has completed.
/// This does not need to be done within the `handleRequest` method.
public class Routing {
	
	/// The routes which have been configured.
	static public var Routes = RouteMap()
	
	private init() {}
	
	/// Handle the request, triggering the routing system.
	/// If a route is discovered the request is sent to the new handler.
	public static func handleRequest(_ request: WebRequest, response: WebResponse) {
		let pathInfo = request.requestURI?.characters.split(separator: "?").map { String($0) }.first ?? "/"
		
		if let handler = Routing.Routes[pathInfo, response] {
			handler(request, response)
		} else {
			response.setStatus(code: 404, message: "NOT FOUND")
			response.appendBody(string: "The file \(pathInfo) was not found.")
			response.requestCompleted()
		}
	}
	
}

class RouteNode: CustomStringConvertible {
	
	#if swift(>=3.0)
	typealias ComponentGenerator = IndexingIterator<[String]>
	#else<commit_msg>Added defult wildcard route to serve static files out of webroot<commit_after>//
//  Routing.swift
//  PerfectLib
//
//  Created by <NAME> on 2015-12-11.
//  Copyright © 2015 PerfectlySoft. All rights reserved.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//


/// Holds the registered routes.
public struct RouteMap: CustomStringConvertible {
	
	public typealias RequestHandler = (WebRequest, WebResponse) -> ()
	
	/// Pretty prints all route information.
	public var description: String {
		var s = self.root.description
		for (method, root) in self.methodRoots {
			s.append("\n" + method + ":\n" + root.description)
		}
		return s
	}
	
	private let root = RouteNode() // root node for any request method
	private var methodRoots = [String:RouteNode]() // by convention, use all upper cased method names for inserts/lookups
	
	// Lookup a route based on the URL path.
	// Returns the handler generator if found.
	subscript(path: String, webResponse: WebResponse) -> RequestHandler? {
		get {
			let components = path.lowercased().pathComponents
			var g = components.makeIterator()
			let _ = g.next() // "/"
			
			let method = webResponse.request.requestMethod!.uppercased()
			if let root = self.methodRoots[method] {
				if let handler = root.findHandler(currentComponent: "", generator: g, webResponse: webResponse) {
					return handler
				}
			}
			return self.root.findHandler(currentComponent: "", generator: g, webResponse: webResponse)
		}
	}
	
	/// Add a route to the system.
	/// `Routing.Routes["/foo/*/baz"] = { _ in return ExampleHandler() }`
	public subscript(path: String) -> RequestHandler? {
		get {
			return nil // Swift does not currently allow set-only subscripts
		}
		set {
			self.root.addPathSegments(generator: path.lowercased().pathComponents.makeIterator(), handler: newValue!)
		}
	}
	
	/// Add an array of routes for a given handler.
	/// `Routing.Routes[ ["/", "index.html"] ] = { _ in return ExampleHandler() }`
	public subscript(paths: [String]) -> RequestHandler? {
		get {
			return nil
		}
		set {
			for path in paths {
				self[path] = newValue
			}
		}
	}
	
	/// Add a route to the system using the indicated HTTP request method.
	/// `Routing.Routes["GET", "/foo/*/baz"] = { _ in return ExampleHandler() }`
	public subscript(method: String, path: String) -> RequestHandler? {
		get {
			return nil // Swift does not currently allow set-only subscripts
		}
		set {
			let uppered = method.uppercased()
			if let root = self.methodRoots[uppered] {
				root.addPathSegments(generator: path.lowercased().pathComponents.makeIterator(), handler: newValue!)
			} else {
				let root = RouteNode()
				self.methodRoots[uppered] = root
				root.addPathSegments(generator: path.lowercased().pathComponents.makeIterator(), handler: newValue!)
			}
		}
	}
	
	/// Add an array of routes for a given handler using the indicated HTTP request method.
	/// `Routing.Routes["GET", ["/", "index.html"] ] = { _ in return ExampleHandler() }`
	public subscript(method: String, paths: [String]) -> RequestHandler? {
		get {
			return nil // Swift does not currently allow set-only subscripts
		}
		set {
			for path in paths {
				self[method, path] = newValue
			}
		}
	}
}

/// This wraps up the routing related functionality.
/// Enable the routing system by calling:
/// ```
/// Routing.Handler.registerGlobally()
/// ```
/// This should be done in your `PerfectServerModuleInit` function.
/// The system supports HTTP method based routing, wildcards and variables.
///
/// Add routes in the following manner:
/// ```
/// 	Routing.Routes["GET", ["/", "index.html"] ] = { (_:WebResponse) in return IndexHandler() }
/// 	Routing.Routes["/foo/*/baz"] = { _ in return EchoHandler() }
/// 	Routing.Routes["/foo/bar/baz"] = { _ in return EchoHandler() }
/// 	Routing.Routes["GET", "/user/{id}/baz"] = { _ in return Echo2Handler() }
/// 	Routing.Routes["POST", "/user/{id}/baz"] = { _ in return Echo3Handler() }
/// ```
/// The closure you provide should return an instance of `PageHandler`. It is provided the WebResponse object to permit further customization.
/// Variables set by the routing process can be accessed through the `WebRequest.urlVariables` dictionary.
/// Note that a PageHandler *MUST* call `WebResponse.requestCompleted()` when the request has completed.
/// This does not need to be done within the `handleRequest` method.
public struct Routing {
	
	/// The routes which have been configured.
	static public var Routes = RouteMap()
	
	static func initialize() {
		// add a wildcard handler for webroot access
		// user modules can overwrite this if desired
		Routing.Routes["*"] = {
			request, response in
			
			StaticFileHandler().handleRequest(request: request, response: response)
		}
	}
	
	private init() {}
	
	/// Handle the request, triggering the routing system.
	/// If a route is discovered the request is sent to the new handler.
	public static func handleRequest(_ request: WebRequest, response: WebResponse) {
		let pathInfo = request.requestURI?.characters.split(separator: "?").map { String($0) }.first ?? "/"
		
		if let handler = Routing.Routes[pathInfo, response] {
			handler(request, response)
		} else {
			response.setStatus(code: 404, message: "NOT FOUND")
			response.appendBody(string: "The file \(pathInfo) was not found.")
			response.requestCompleted()
		}
	}
}

class RouteNode: CustomStringConvertible {
	
	#if swift(>=3.0)
	typealias ComponentGenerator = IndexingIterator<[String]>
	#else
{'fraction_non_alphanumeric': 0.15680759426913948, 'fraction_numerical': 0.005273798013536082, 'mean_word_length': 3.0276106194690264, 'pattern_counts': {'":': 2, '<': 7, '<?xml version=': 0, '>': 21, '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': '3287924', 'n_tokens_mistral': 3604, 'n_tokens_neox': 3245, 'n_words': 1058}