Skip to content

Commit ef17250

Browse files
streaming now works as expected (with 1 discovered issue)
1 parent 7379ac0 commit ef17250

File tree

3 files changed

+121
-15
lines changed

3 files changed

+121
-15
lines changed

Sources/HTMLKitParse/ExpandHTMLMacro.swift

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,11 +238,17 @@ extension HTMLKitUtilities {
238238
if async {
239239
string += "Task {\n"
240240
}
241+
let duration:String?
242+
if let suspendDuration {
243+
duration = durationDebugDescription(suspendDuration)
244+
} else {
245+
duration = nil
246+
}
241247
let chunks = chunks(encoding: encoding, encodedResult: encodedResult, async: async, optimized: optimized, chunkSize: chunkSize)
242248
for chunk in chunks {
243249
string += "continuation.yield(" + chunk + ")\n"
244-
if let suspendDuration {
245-
string += "try await Task.sleep(for: \(suspendDuration))\n"
250+
if let duration {
251+
string += "try await Task.sleep(for: \(duration))\n"
246252
}
247253
}
248254
string += "continuation.finish()\n}"
@@ -251,4 +257,29 @@ extension HTMLKitUtilities {
251257
}
252258
return string
253259
}
260+
static func durationDebugDescription(_ duration: Duration) -> String {
261+
let (seconds, attoseconds) = duration.components
262+
var string:String
263+
if attoseconds == 0 {
264+
string = ".seconds(\(seconds))"
265+
} else {
266+
var nanoseconds = attoseconds / 1_000_000_000
267+
nanoseconds += seconds * 1_000_000_000
268+
string = "\(nanoseconds)"
269+
if seconds == 0 {
270+
if string.hasSuffix("000000") {
271+
string.removeLast(6)
272+
string = ".milliseconds(\(string))"
273+
} else if string.hasSuffix("000") {
274+
string.removeLast(3)
275+
string = ".microseconds(\(string))"
276+
} else {
277+
string = ".nanoseconds(\(string))"
278+
}
279+
} else {
280+
string = ".nanoseconds(\(nanoseconds))"
281+
}
282+
}
283+
return string
284+
}
254285
}

Sources/HTMLKitParse/ParseData.swift

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -127,20 +127,41 @@ extension HTMLKitUtilities {
127127
chunkSize = size
128128
}
129129
case "suspendDuration":
130-
// TODO: support
131-
if let member = arg.expression.memberAccess?.declName.baseName.text {
132-
switch member {
133-
case "milliseconds":
134-
break
135-
case "microseconds":
136-
break
137-
case "nanoseconds":
138-
break
139-
case "seconds":
140-
break
141-
default:
142-
break
130+
guard let function = arg.expression.functionCall else { break }
131+
var intValue:UInt64? = nil
132+
var doubleValue:Double? = nil
133+
if let v = function.arguments.first?.expression.integerLiteral?.literal.text, let i = UInt64(v) {
134+
intValue = i
135+
} else if let v = function.arguments.first?.expression.as(FloatLiteralExprSyntax.self)?.literal.text, let d = Double(v) {
136+
doubleValue = d
137+
} else {
138+
break
139+
}
140+
switch function.calledExpression.memberAccess?.declName.baseName.text {
141+
case "milliseconds":
142+
if let intValue {
143+
suspendDuration = .milliseconds(intValue)
144+
} else if let doubleValue {
145+
suspendDuration = .milliseconds(doubleValue)
146+
}
147+
case "microseconds":
148+
if let intValue {
149+
suspendDuration = .microseconds(intValue)
150+
} else if let doubleValue {
151+
suspendDuration = .microseconds(doubleValue)
152+
}
153+
case "nanoseconds":
154+
if let intValue {
155+
suspendDuration = .nanoseconds(intValue)
156+
}
157+
case "seconds":
158+
if let intValue {
159+
suspendDuration = .seconds(intValue)
160+
} else if let doubleValue {
161+
suspendDuration = .seconds(doubleValue)
143162
}
163+
default:
164+
break
144165
}
145166
default:
146167
break
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
2+
#if compiler(>=6.0)
3+
4+
import HTMLKit
5+
import Testing
6+
7+
struct StreamTests {
8+
@Test(.timeLimit(.minutes(1)))
9+
func streamTest() async {
10+
let expected:String = #html(
11+
html {
12+
body {
13+
div()
14+
div()
15+
div()
16+
div()
17+
div()
18+
div()
19+
div()
20+
div()
21+
div()
22+
div()
23+
}
24+
}
25+
)
26+
// TODO: fix infinite loop if `chunkSize` is `40`.
27+
let test:AsyncStream<String> = #html(representation: .streamedAsync(chunkSize: 50, suspendDuration: .milliseconds(5))) {
28+
html {
29+
body {
30+
div()
31+
div()
32+
div()
33+
div()
34+
div()
35+
div()
36+
div()
37+
div()
38+
div()
39+
div()
40+
}
41+
}
42+
}
43+
var receivedHTML = ""
44+
let now = ContinuousClock.now
45+
for await test in test {
46+
receivedHTML += test
47+
}
48+
let took = ContinuousClock.now - now
49+
#expect(took < .milliseconds(25))
50+
#expect(receivedHTML == expected)
51+
}
52+
}
53+
54+
#endif

0 commit comments

Comments
 (0)