11import Foundation
2+ @_spi ( Internal) import _Helpers
23
34#if canImport(FoundationNetworking)
45 import FoundationNetworking
56#endif
67
78/// The builder class for creating and executing requests to a PostgREST server.
8- public class PostgrestBuilder {
9+ public class PostgrestBuilder : @ unchecked Sendable {
910 /// The configuration for the PostgREST client.
1011 let configuration : PostgrestClient . Configuration
11- /// The URL for the request.
12- let url : URL
13- /// The query parameters for the request.
14- var queryParams : [ ( name: String , value: String ? ) ]
15- /// The headers for the request.
16- var headers : [ String : String ]
17- /// The HTTP method for the request.
18- var method : String
19- /// The body data for the request.
20- var body : Data ?
21-
22- /// The options for fetching data from the PostgREST server.
23- var fetchOptions = FetchOptions ( )
12+
13+ struct MutableState {
14+ var request : Request
15+
16+ /// The options for fetching data from the PostgREST server.
17+ var fetchOptions : FetchOptions
18+ }
19+
20+ let mutableState : ActorIsolated < MutableState >
2421
2522 init (
2623 configuration: PostgrestClient . Configuration ,
27- url: URL ,
28- queryParams: [ ( name: String , value: String ? ) ] ,
29- headers: [ String : String ] ,
30- method: String ,
31- body: Data ?
24+ request: Request
3225 ) {
3326 self . configuration = configuration
34- self . url = url
35- self . queryParams = queryParams
36- self . headers = headers
37- self . method = method
38- self . body = body
27+
28+ mutableState = ActorIsolated (
29+ MutableState (
30+ request: request,
31+ fetchOptions: FetchOptions ( )
32+ )
33+ )
3934 }
4035
4136 convenience init ( _ other: PostgrestBuilder ) {
4237 self . init (
4338 configuration: other. configuration,
44- url: other. url,
45- queryParams: other. queryParams,
46- headers: other. headers,
47- method: other. method,
48- body: other. body
39+ request: other. mutableState. value. request
4940 )
5041 }
5142
@@ -57,7 +48,10 @@ public class PostgrestBuilder {
5748 public func execute(
5849 options: FetchOptions = FetchOptions ( )
5950 ) async throws -> PostgrestResponse < Void > {
60- fetchOptions = options
51+ mutableState. withValue {
52+ $0. fetchOptions = options
53+ }
54+
6155 return try await execute { _ in ( ) }
6256 }
6357
@@ -69,39 +63,40 @@ public class PostgrestBuilder {
6963 public func execute< T: Decodable > (
7064 options: FetchOptions = FetchOptions ( )
7165 ) async throws -> PostgrestResponse < T > {
72- fetchOptions = options
66+ mutableState. withValue {
67+ $0. fetchOptions = options
68+ }
69+
7370 return try await execute { [ configuration] data in
7471 try configuration. decoder. decode ( T . self, from: data)
7572 }
7673 }
7774
78- func appendSearchParams( name: String , value: String ) {
79- queryParams. append ( ( name, value) )
80- }
81-
8275 private func execute< T> ( decode: ( Data ) throws -> T ) async throws -> PostgrestResponse < T > {
83- if fetchOptions. head {
84- method = " HEAD "
85- }
86-
87- if let count = fetchOptions. count {
88- if let prefer = headers [ " Prefer " ] {
89- headers [ " Prefer " ] = " \( prefer) ,count= \( count. rawValue) "
90- } else {
91- headers [ " Prefer " ] = " count= \( count. rawValue) "
76+ mutableState. withValue {
77+ if $0. fetchOptions. head {
78+ $0. request. method = " HEAD "
9279 }
93- }
9480
95- if headers [ " Accept " ] == nil {
96- headers [ " Accept " ] = " application/json "
97- }
98- headers [ " Content-Type " ] = " application/json "
81+ if let count = $0. fetchOptions. count {
82+ if let prefer = $0. request. headers [ " Prefer " ] {
83+ $0. request. headers [ " Prefer " ] = " \( prefer) ,count= \( count. rawValue) "
84+ } else {
85+ $0. request. headers [ " Prefer " ] = " count= \( count. rawValue) "
86+ }
87+ }
9988
100- if let schema = configuration. schema {
101- if method == " GET " || method == " HEAD " {
102- headers [ " Accept-Profile " ] = schema
103- } else {
104- headers [ " Content-Profile " ] = schema
89+ if $0. request. headers [ " Accept " ] == nil {
90+ $0. request. headers [ " Accept " ] = " application/json "
91+ }
92+ $0. request. headers [ " Content-Type " ] = " application/json "
93+
94+ if let schema = configuration. schema {
95+ if $0. request. method == " GET " || $0. request. method == " HEAD " {
96+ $0. request. headers [ " Accept-Profile " ] = schema
97+ } else {
98+ $0. request. headers [ " Content-Profile " ] = schema
99+ }
105100 }
106101 }
107102
@@ -122,13 +117,18 @@ public class PostgrestBuilder {
122117 }
123118
124119 private func makeURLRequest( ) throws -> URLRequest {
125- guard var components = URLComponents ( url: url, resolvingAgainstBaseURL: false ) else {
120+ let request = mutableState. value. request
121+
122+ guard var components = URLComponents (
123+ url: configuration. url. appendingPathComponent ( request. path) ,
124+ resolvingAgainstBaseURL: false
125+ ) else {
126126 throw URLError ( . badURL)
127127 }
128128
129- if !queryParams . isEmpty {
129+ if !request . query . isEmpty {
130130 let percentEncodedQuery =
131- ( components. percentEncodedQuery. map { $0 + " & " } ?? " " ) + query( queryParams )
131+ ( components. percentEncodedQuery. map { $0 + " & " } ?? " " ) + query( request . query )
132132 components. percentEncodedQuery = percentEncodedQuery
133133 }
134134
@@ -138,13 +138,13 @@ public class PostgrestBuilder {
138138
139139 var urlRequest = URLRequest ( url: url)
140140
141- for (key, value) in headers {
141+ for (key, value) in request . headers {
142142 urlRequest. setValue ( value, forHTTPHeaderField: key)
143143 }
144144
145- urlRequest. httpMethod = method
145+ urlRequest. httpMethod = request . method
146146
147- if let body {
147+ if let body = request . body {
148148 urlRequest. httpBody = body
149149 }
150150
@@ -155,17 +155,17 @@ public class PostgrestBuilder {
155155 string. addingPercentEncoding ( withAllowedCharacters: . postgrestURLQueryAllowed) ?? string
156156 }
157157
158- private func query( _ parameters: [ ( String , String ? ) ] ) -> String {
159- parameters. compactMap { key , value in
160- if let value {
161- return ( key , value)
158+ private func query( _ parameters: [ URLQueryItem ] ) -> String {
159+ parameters. compactMap { query in
160+ if let value = query . value {
161+ return ( query . name , value)
162162 }
163163 return nil
164164 }
165- . map { key , value -> String in
166- let escapedKey = escape ( key )
165+ . map { name , value -> String in
166+ let escapedName = escape ( name )
167167 let escapedValue = escape ( value)
168- return " \( escapedKey ) = \( escapedValue) "
168+ return " \( escapedName ) = \( escapedValue) "
169169 }
170170 . joined ( separator: " & " )
171171 }
0 commit comments