@@ -5,39 +5,34 @@ import StructuredQueriesSupport
55/// You will typically create instances of this type using string literals, where bindings are
66/// directly interpolated into the string. This most commonly occurs when using the `#sql` macro,
77/// which takes values of this type.
8- public struct QueryFragment : Hashable , Sendable , CustomDebugStringConvertible {
9- #if DEBUG
10- /// The underlying SQL string.
11- public var string : String
12- #else
13- /// The underlying SQL string.
14- public package ( set) var string : String
15- #endif
8+ public struct QueryFragment : Hashable , Sendable {
9+ // TODO: Call this 'Element' and make 'QueryFragment' a collection of them?
10+ public enum Segment : Hashable , Sendable {
11+ case sql( String )
12+ case binding( QueryBinding )
13+ }
14+
15+ // TODO: Make 'private(set)' and add APIs to support extensibility like 'indent()'?
16+ public internal( set) var segments : [ Segment ] = [ ]
1617
17- #if DEBUG
18- /// An array of parameterized statement bindings.
19- public var bindings : [ QueryBinding ]
20- #else
21- /// An array of parameterized statement bindings.
22- public package ( set) var bindings : [ QueryBinding ]
23- #endif
18+ fileprivate init ( segments: [ Segment ] ) {
19+ self . segments = segments
20+ }
2421
25- init ( _ string: String = " " , _ bindings: [ QueryBinding ] = [ ] ) {
26- self . string = string
27- self . bindings = bindings
22+ init ( _ string: String = " " ) {
23+ self . init ( segments: [ . sql( string) ] )
2824 }
2925
3026 /// A Boolean value indicating whether the query fragment is empty.
3127 public var isEmpty : Bool {
32- return string . isEmpty && bindings . isEmpty
28+ segments . isEmpty
3329 }
3430
3531 /// Appends the given fragment to this query fragment.
3632 ///
3733 /// - Parameter other: Another query fragment.
3834 public mutating func append( _ other: Self ) {
39- string. append ( other. string)
40- bindings. append ( contentsOf: other. bindings)
35+ segments. append ( contentsOf: other. segments)
4136 }
4237
4338 /// Appends a given query fragment to another fragment.
@@ -51,36 +46,18 @@ public struct QueryFragment: Hashable, Sendable, CustomDebugStringConvertible {
5146 query += rhs
5247 return query
5348 }
49+ }
5450
51+ extension QueryFragment : CustomDebugStringConvertible {
5552 public var debugDescription : String {
56- var compiled = " "
57- var bindings = bindings
58- var currentDelimiter : Character ?
59- compiled. reserveCapacity ( string. count)
60- let delimiters : [ Character : Character ] = [
61- #"""# : #"""# ,
62- " ' " : " ' " ,
63- " ` " : " ` " ,
64- " [ " : " ] " ,
65- ]
66- for character in string {
67- if let delimiter = currentDelimiter {
68- if delimiter == character,
69- compiled. last != character || compiled. last == delimiters [ delimiter]
70- {
71- currentDelimiter = nil
72- }
73- compiled. append ( character)
74- } else if delimiters. keys. contains ( character) {
75- currentDelimiter = character
76- compiled. append ( character)
77- } else if character == " ? " {
78- compiled. append ( bindings. removeFirst ( ) . debugDescription)
79- } else {
80- compiled. append ( character)
53+ segments. reduce ( into: " " ) { debugDescription, segment in
54+ switch segment {
55+ case . sql( let sql) :
56+ debugDescription. append ( sql)
57+ case . binding( let binding) :
58+ debugDescription. append ( binding. debugDescription)
8159 }
8260 }
83- return compiled
8461 }
8562}
8663
@@ -103,7 +80,7 @@ extension [QueryFragment] {
10380
10481extension QueryFragment : ExpressibleByStringInterpolation {
10582 public init ( stringInterpolation: StringInterpolation ) {
106- self . init ( stringInterpolation . string , stringInterpolation. bindings )
83+ self . init ( segments : stringInterpolation. segments )
10784 }
10885
10986 public init ( stringLiteral value: String ) {
@@ -132,16 +109,15 @@ extension QueryFragment: ExpressibleByStringInterpolation {
132109 }
133110
134111 public struct StringInterpolation : StringInterpolationProtocol {
135- public var string = " "
136- public var bindings : [ QueryBinding ] = [ ]
112+ fileprivate var segments : [ Segment ] = [ ]
137113
138114 public init ( literalCapacity: Int , interpolationCount: Int ) {
139- string . reserveCapacity ( literalCapacity )
140- bindings . reserveCapacity ( interpolationCount)
115+ // TODO: Should all the segments' strings share the same contiguous storage as substring/span?
116+ segments . reserveCapacity ( interpolationCount)
141117 }
142118
143119 public mutating func appendLiteral( _ literal: String ) {
144- string . append ( literal)
120+ segments . append ( . sql ( literal) )
145121 }
146122
147123 /// Append a quoted fragment to the interpolation.
@@ -162,7 +138,7 @@ extension QueryFragment: ExpressibleByStringInterpolation {
162138 quote sql: String ,
163139 delimiter: QuoteDelimiter = . identifier
164140 ) {
165- string . append ( sql. quoted ( delimiter) )
141+ segments . append ( . sql( sql . quoted ( delimiter) ) )
166142 }
167143
168144 /// Append a raw SQL string to the interpolation.
@@ -174,7 +150,7 @@ extension QueryFragment: ExpressibleByStringInterpolation {
174150 ///
175151 /// - Parameter sql: A raw query string.
176152 public mutating func appendInterpolation( raw sql: String ) {
177- string . append ( sql)
153+ segments . append ( . sql( sql ) )
178154 }
179155
180156 /// Append a raw lossless string to the interpolation.
@@ -191,23 +167,21 @@ extension QueryFragment: ExpressibleByStringInterpolation {
191167 ///
192168 /// - Parameter sql: A raw query string.
193169 public mutating func appendInterpolation( raw sql: some LosslessStringConvertible ) {
194- string . append ( sql. description)
170+ segments . append ( . sql( sql . description) )
195171 }
196172
197173 /// Append a query binding to the interpolation.
198174 ///
199175 /// - Parameter binding: A query binding.
200176 public mutating func appendInterpolation( _ binding: QueryBinding ) {
201- string. append ( " ? " )
202- bindings. append ( binding)
177+ segments. append ( . binding( binding) )
203178 }
204179
205180 /// Append a query fragment to the interpolation.
206181 ///
207182 /// - Parameter fragment: A query fragment.
208183 public mutating func appendInterpolation( _ fragment: QueryFragment ) {
209- string. append ( fragment. string)
210- bindings. append ( contentsOf: fragment. bindings)
184+ segments. append ( contentsOf: fragment. segments)
211185 }
212186
213187 /// Append a query expression to the interpolation.
0 commit comments