-
-
Notifications
You must be signed in to change notification settings - Fork 159
/
ConfigurationPatternConvertible.swift
130 lines (107 loc) · 4.57 KB
/
ConfigurationPatternConvertible.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//
// ConfigurationPatternConvertible.swift
// Siesta
//
// Created by Paul on 2016/4/22.
// Copyright © 2016 Bust Out Solutions. All rights reserved.
//
import Foundation
/**
A type that can serve as a URL matcher for service configuration.
Siesta provides implementations of this protocol for `String` (for glob-based matching) and `Resource` (to configure
one specific resource).
- SeeAlso: `Service.configure(_:description:configurer:)`
- SeeAlso: `String.configurationPattern(_:)`
- SeeAlso: `Resource.configurationPattern(_:)`
*/
public protocol ConfigurationPatternConvertible
{
/// Turns the receiver into a predicate that matches URLs.
func configurationPattern(service: Service) -> NSURL -> Bool
/// A logging-friendly description of the receiver when it acts as a URL pattern.
var configurationPatternDescription: String { get }
}
/**
Support for passing URL patterns with wildcards to `Service.configure(...)`.
*/
extension String: ConfigurationPatternConvertible
{
/**
Matches URLs using shell-like wildcards / globs.
The `urlPattern` is interpreted relative to the service’s base URL unless it begins with a protocol (e.g. `http:`).
If it is relative, the leading slash is optional.
The pattern supports three wildcards:
- `*` matches zero or more characters within a path segment.
- `**` matches zero or more characters across path segments, with the special case that `/**/` matches `/`.
- `?` matches exactly one character within a path segment, and thus `?*` matches one or more.
Examples:
- `/foo/*/bar` matches `/foo/1/bar` and `/foo/123/bar`.
- `/foo/**/bar` matches `/foo/bar`, `/foo/123/bar`, and `/foo/1/2/3/bar`.
- `/foo*/bar` matches `/foo/bar` and `/food/bar`.
- `/foo/*` matches `/foo/123` and `/foo/`.
- `/foo/?*` matches `/foo/123` but _not_ `/foo/`.
The pattern ignores the resource’s query string.
*/
public func configurationPattern(service: Service) -> NSURL -> Bool
{
// If the pattern has a URL protocol (e.g. "http:"), interpret it as absolute.
// If the service has no baseURL, interpret the pattern as absolure.
// Otherwise, interpret pattern as relative to baseURL.
let resolvedPattern: String
if let prefix = service.baseURL?.absoluteString where !containsRegex("^[a-z]+:")
{ resolvedPattern = prefix + stripPrefix("/") }
else
{ resolvedPattern = self }
let pattern = NSRegularExpression.compile(
"^"
+ NSRegularExpression.escapedPatternForString(resolvedPattern)
.replacingString("\\*\\*\\/", "([^:?]*/|)")
.replacingString("\\*\\*", "[^:?]*")
.replacingString("\\*", "[^/:?]*")
.replacingString("\\?", "[^/:?]")
+ "($|\\?)")
debugLog(.Configuration, ["URL pattern", self, "compiles to regex", pattern.pattern])
return pattern.configurationPattern(service)
}
/// :nodoc:
public var configurationPatternDescription: String
{ return self }
}
/**
Support for passing regular expressions to `Service.configure(...)`.
*/
extension NSRegularExpression: ConfigurationPatternConvertible
{
/**
Matches URLs if this regular expression matches any substring of the URL’s full, absolute form.
Note that, unlike the simpler wildcard form of `String.configurationPattern(_:)`, the regular expression is _not_
matched relative to the Service’s base URL. The match is performed against the full URL: scheme, host, path,
query string and all.
Note also that this implementation matches substrings. Include `^` and `$` if you want your pattern to match
against the entire URL.
*/
public func configurationPattern(service: Service) -> NSURL -> Bool
{
return { self.matches($0.absoluteString) }
}
/// :nodoc:
public var configurationPatternDescription: String
{ return pattern }
}
/**
Support for passing a specific `Resource` to `Service.configure(...)`.
*/
extension Resource: ConfigurationPatternConvertible
{
/**
Matches this specific resource when passed as a pattern to `Service.configure(...)`.
*/
public func configurationPattern(service: Service) -> NSURL -> Bool
{
let resourceURL = url // prevent resource capture in closure
return { $0 == resourceURL }
}
/// :nodoc:
public var configurationPatternDescription: String
{ return url.absoluteString }
}