swift-url-routing icon indicating copy to clipboard operation
swift-url-routing copied to clipboard

Ignoring path components

Open KunzManuel opened this issue 2 years ago • 2 comments

Hello everyone 👋

We are currently using URLRouting in our Application. In our current case we've been facing a problem with the parsing of the path of an URL.

Use case:

The link which will be parsed looks like this: https://example.de/colors

In some cases this link can have multiple path components. The number of path components is not fixed and we do not know how many additional path components are added to this link (0..n).

  • https://example.de/colors/foo
  • https://example.de/colors/foo/bar

Is there any way this can be done. We tried multiple combinations with Skip, Many and Rest but the problem seems to be that the Path component passes one element to the Parser at a time.

The workaround for us would be to implement a custom Path parser which does not pass the path components one by one but the whole path at once. So we can work with the Prefix Parser. We'd like to know if there is a better way with the given component?

Thank you for having a look at this. 👍

Following a code example to represent the problem.

import Foundation
import URLRouting

enum AppRoute {
    case colors
}


let appRouter = OneOf {
    Route(.case(AppRoute.colors)) {
        Scheme("https")
        Host("example.de")
        Path {
            "colors"
        }
    }
    
}

let testURL = URL(string: "https://example.de/colors")!
let testURL2 = URL(string: "https://example.de/colors/foo")!
try appRouter.match(url: testURL) // matches AppRoute.colors
try appRouter.match(url: testURL2) // fails with expected end of input`

KunzManuel avatar Jun 29 '23 14:06 KunzManuel

I am also having this issue. Did you manage to get this working in the end?

oliverfoggin avatar Aug 02 '23 10:08 oliverfoggin

Unfortunately not with the given Parsers. For me the solution as to write a customer parser which checks for the prefix only and consumes the path.

struct PrefixPath: ParserPrinter {
    @usableFromInline
    var pathPrefix: String

    /// Initializes a prefix path parser.
    /// Validates if a given path starts with the prefix. Path after the prefix will be ignored.
    /// - Parameter pathPrefix: The prefix which will be
    @inlinable
    init(_ pathPrefix: String) {
        self.pathPrefix = pathPrefix
    }

    @inlinable
    func parse(_ input: inout URLRequestData) throws {
        guard input.path.count >= 1 else { throw PrefixPathRoutingError() }
        let path = input.path.joined(separator: "/").lowercased()

        guard path.hasPrefix(pathPrefix.lowercased()) else {
            throw PrefixPathRoutingError()
        }
        input.path = []
    }

    @inlinable
    func print(_: (), into input: inout URLRequestData) {
        input.path = pathPrefix.split(separator: "/")[...]
    }
}

@usableFromInline
struct PrefixPathRoutingError: Error {
    @usableFromInline
    init() {}
}

KunzManuel avatar Aug 02 '23 11:08 KunzManuel