Skip to content

Commit e839f7e

Browse files
authored
Add new extensible type CacheControlValue (#686)
1 parent 8976514 commit e839f7e

File tree

1 file changed

+113
-2
lines changed

1 file changed

+113
-2
lines changed

Sources/Hummingbird/Files/CacheControl.swift

+113-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414

1515
/// Associates cache control values with filename
1616
public struct CacheControl: Sendable {
17+
/// Cache control directive
18+
///
19+
/// Original CacheControl directive value was a fixed enum. This has been replaced
20+
/// with CacheControl.CacheControlValue which is more extensible
21+
@_documentation(visibility: internal)
1722
public enum Value: CustomStringConvertible, Sendable {
1823
case noStore
1924
case noCache
@@ -38,12 +43,118 @@ public struct CacheControl: Sendable {
3843
return "must-revalidate"
3944
}
4045
}
46+
47+
var cacheControlValue: CacheControlValue {
48+
switch self {
49+
case .noStore: .noStore
50+
case .noCache: .noCache
51+
case .private: .private
52+
case .public: .public
53+
case .maxAge(let value): .maxAge(value)
54+
case .mustRevalidate: .mustRevalidate
55+
}
56+
}
57+
}
58+
59+
/// Cache control directive
60+
public struct CacheControlValue: CustomStringConvertible, Sendable {
61+
private enum Value: CustomStringConvertible, Sendable {
62+
case noStore
63+
case noCache
64+
case `private`
65+
case `public`
66+
case maxAge(Int)
67+
case mustRevalidate
68+
case mustUnderstand
69+
case noTransform
70+
case immutable
71+
case custom(String)
72+
73+
public var description: String {
74+
switch self {
75+
case .noStore:
76+
return "no-store"
77+
case .noCache:
78+
return "no-cache"
79+
case .private:
80+
return "private"
81+
case .public:
82+
return "public"
83+
case .maxAge(let amount):
84+
return "max-age=\(amount)"
85+
case .mustRevalidate:
86+
return "must-revalidate"
87+
case .mustUnderstand:
88+
return "must-understand"
89+
case .noTransform:
90+
return "no-transform"
91+
case .immutable:
92+
return "immutable"
93+
case .custom(let string):
94+
return string
95+
}
96+
}
97+
}
98+
private let value: Value
99+
100+
/// The no-store response directive indicates that any caches of any kind (private or shared) should not
101+
/// store this response.
102+
public static var noStore: Self { .init(value: .noStore) }
103+
/// The no-cache response directive indicates that the response can be stored in caches, but the response
104+
/// must be validated with the origin server before each reuse, even when the cache is disconnected from
105+
/// the origin server.
106+
public static var noCache: Self { .init(value: .noCache) }
107+
/// The private response directive indicates that the response can be stored only in a private cache (e.g.
108+
/// local caches in browsers).
109+
public static var `private`: Self { .init(value: .private) }
110+
/// The public response directive indicates that the response can be stored in a shared cache. Responses
111+
/// for requests with Authorization header fields must not be stored in a shared cache; however, the public
112+
/// directive will cause such responses to be stored in a shared cache.
113+
public static var `public`: Self { .init(value: .public) }
114+
/// The max-age=N response directive indicates that the response remains fresh until N seconds after the
115+
/// response is generated.
116+
public static func maxAge(_ amount: Int) -> Self { .init(value: .maxAge(amount)) }
117+
/// The must-revalidate response directive indicates that the response can be stored in caches and can be
118+
/// reused while fresh. If the response becomes stale, it must be validated with the origin server before reuse.
119+
public static var mustRevalidate: Self { .init(value: .mustRevalidate) }
120+
/// The must-understand response directive indicates that a cache should store the response only if it
121+
/// understands the requirements for caching based on status code.
122+
public static var mustUnderstand: Self { .init(value: .mustUnderstand) }
123+
/// Some intermediaries transform content for various reasons. For example, some convert images to reduce
124+
/// transfer size. In some cases, this is undesirable for the content provider.
125+
///
126+
/// no-transform indicates that any intermediary (regardless of whether it implements a cache) shouldn't transform
127+
/// the response contents.
128+
public static var noTransform: Self { .init(value: .noTransform) }
129+
/// The immutable response directive indicates that the response will not be updated while it's fresh.
130+
public static var immutable: Self { .init(value: .immutable) }
131+
/// Custom directive
132+
public static func custom(_ string: String) -> Self { .init(value: .custom(string)) }
133+
134+
public var description: String { value.description }
41135
}
42136

43137
/// Initialize cache control
44138
/// - Parameter entries: cache control entries
139+
@_disfavoredOverload
45140
public init(_ entries: [(MediaType, [Value])]) {
46-
self.entries = entries.map { .init(mediaType: $0.0, cacheControl: $0.1) }
141+
self.entries = entries.map {
142+
.init(
143+
mediaType: $0.0,
144+
cacheControl: $0.1.map { $0.cacheControlValue }
145+
)
146+
}
147+
}
148+
149+
/// Initialize cache control
150+
/// - Parameter entries: cache control entries
151+
public init(_ entries: [(MediaType, [CacheControlValue])]) {
152+
self.entries = entries.map {
153+
.init(
154+
mediaType: $0.0,
155+
cacheControl: $0.1.map { $0 }
156+
)
157+
}
47158
}
48159

49160
/// Get the Cache-Control header for a file
@@ -62,7 +173,7 @@ public struct CacheControl: Sendable {
62173

63174
private struct Entry: Sendable {
64175
let mediaType: MediaType
65-
let cacheControl: [Value]
176+
let cacheControl: [CacheControlValue]
66177
}
67178

68179
private let entries: [Entry]

0 commit comments

Comments
 (0)