Skip to content

Commit

Permalink
RangeReplaceableCollection
Browse files Browse the repository at this point in the history
  • Loading branch information
swhitty committed Apr 12, 2024
1 parent 2ef3a9c commit b55629d
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 3 deletions.
39 changes: 36 additions & 3 deletions FlyingFox/Sources/Handlers/RoutedHTTPHandler.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// CompositeHTTPHandler.swift
// RoutedHTTPHandler.swift
// FlyingFox
//
// Created by Simon Whitty on 25/02/2022.
Expand Down Expand Up @@ -36,12 +36,24 @@ public struct RoutedHTTPHandler: HTTPHandler, Sendable {
public init() { }

public mutating func appendRoute(_ route: HTTPRoute, to handler: some HTTPHandler) {
handlers.append((route, handler))
append((route, handler))
}

public mutating func appendRoute(_ route: HTTPRoute,
handler: @Sendable @escaping (HTTPRequest) async throws -> HTTPResponse) {
handlers.append((route, ClosureHTTPHandler(handler)))
append((route, ClosureHTTPHandler(handler)))
}

public mutating func insertRoute(_ route: HTTPRoute,
at index: Index,
to handler: some HTTPHandler) {
insert((route, handler), at: index)
}

public mutating func insertRoute(_ route: HTTPRoute,
at index: Index,
handler: @Sendable @escaping (HTTPRequest) async throws -> HTTPResponse) {
insert((route, ClosureHTTPHandler(handler)), at: index)
}

public func handleRequest(_ request: HTTPRequest) async throws -> HTTPResponse {
Expand All @@ -59,3 +71,24 @@ public struct RoutedHTTPHandler: HTTPHandler, Sendable {
throw HTTPUnhandledError()
}
}

extension RoutedHTTPHandler: RangeReplaceableCollection {
public typealias Index = Array<Element>.Index
public typealias Element = (route: HTTPRoute, handler: any HTTPHandler)

public var startIndex: Index { handlers.startIndex }
public var endIndex: Index { handlers.endIndex }

public subscript(index: Index) -> Element {
get { handlers[index] }
set { handlers[index] = newValue }
}

public func index(after i: Index) -> Index {
handlers.index(after: i)
}

public mutating func replaceSubrange(_ subrange: Range<Index>, with newElements: some Collection<Element>) {
handlers.replaceSubrange(subrange, with: newElements)
}
}
104 changes: 104 additions & 0 deletions FlyingFox/Tests/Handlers/RoutedHTTPHandlerTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//
// RoutedHTTPHandler.swift
// FlyingFox
//
// Created by Simon Whitty on 12/04/2024.
// Copyright © 2024 Simon Whitty. All rights reserved.
//
// Distributed under the permissive MIT license
// Get the latest version from here:
//
// https://github.com/swhitty/FlyingFox
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

import FlyingFox
import XCTest

final class RoutedHTTPHandlerTests: XCTestCase {

func testRoutes_CanBeReplaced() async throws {
// given
var handler = RoutedHTTPHandler()

// when
handler.insertRoute("GET /fish", at: 0, to: MockHandler())
handler.insertRoute("GET /chips", at: 0) { _ in throw HTTPUnhandledError() }

// then
XCTAssertEqual(
handler.map(\.route.stringValue),
["GET /chips", "GET /fish"]
)

// when
handler[1] = ("POST /shrimp", MockHandler())

// then
XCTAssertEqual(
handler.map(\.route.stringValue),
["GET /chips", "POST /shrimp"]
)

// when
handler.replaceSubrange(0..., with: [(HTTPRoute("POST /fish"), MockHandler())])

// then
XCTAssertEqual(
handler.map(\.route.stringValue),
["POST /fish"]
)

// when
handler.removeAll()

// then
XCTAssertTrue(
handler.isEmpty
)
}
}

private struct MockHandler: HTTPHandler {
func handleRequest(_ request: HTTPRequest) async throws -> HTTPResponse {
throw HTTPUnhandledError()
}
}

private extension HTTPRoute {

var stringValue: String {
let method = method.stringValue
let path = path.map(\.stringValue).joined(separator: "/")
return method + " /" + path
}
}

private extension HTTPRoute.Component {

var stringValue: String {
switch self {
case .wildcard:
return "*"
case let .caseInsensitive(pattern):
return pattern
}
}
}

0 comments on commit b55629d

Please sign in to comment.