trace_context.bend
bend-trace-context@0.2.0.0 · 3,907 lines · 415 definitions
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
100import Base
101import bend-trace-context@0.2.0.0/src/hex.bend as Hex
102import bend-trace-context@0.2.0.0/src/digits.bend as Digits
103
104
105
106
107
108
109
110
111
112
113
114type Field is Data:
115 TraceIdField{}
116 ParentIdField{}
117 SpanIdField{}
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142type Error is Data:
143 UnexpectedEnd{offset: Nat}
144 InvalidHex{offset: Nat}
145 InvalidByte{offset: Nat}
146 ExpectedSeparator{offset: Nat}
147 TrailingInput{}
148 ForbiddenVersion{}
149 UnsupportedVersion{version: String}
150 ZeroId{field: Field}
151 ControlCharacter{offset: Nat}
152
153
154
155type ContextError is Data:
156 ReusedSpanId{}
157 ReusedTraceId{}
158
159
160
161
162type Parsed<-n: Nat> is Data:
163 Parsed{digits: Digits.Digits(n), rest: String}
164
165
166
167type ParsedBytes<-n: Nat> is Data:
168 ParsedBytes{digits: Digits.Digits(Nat.double(n)), rest: List<&2, U32>}
169
170
171
172
173
174
175type TraceParentV00 is Data:
176 TraceParentV00{
177 trace_id: Digits.NonZero<32n>,
178 parent_id: Digits.NonZero<16n>,
179 flags: Digits.Digits(2n)
180 }
181
182
183
184
185
186
187type TraceId is Data:
188 TraceId{value: Digits.NonZero<32n>, random: Bool}
189
190
191
192
193type SpanId is Data:
194 SpanId{value: Digits.NonZero<16n>}
195
196
197
198
199type Sampling is Data:
200 InheritSampled{}
201 SetSampled{sampled: Bool}
202
203
204
205
206
207
208type RemoteContext is Data:
209 RemoteContext{trace_id: TraceId, span_id: SpanId, sampled: Bool}
210
211
212
213type LocalContext is Data:
214 LocalContext{trace_id: TraceId, span_id: SpanId, sampled: Bool}
215
216
217
218type Parent is Data:
219 RemoteParent{context: RemoteContext}
220 LocalParent{context: LocalContext}
221
222
223
224
225type GenerationError is Data:
226 SourceFailure{code: U32, message: String}
227 ExhaustedTraceId{}
228 ExhaustedSpanId{}
229
230
231
232type TraceWords is Data:
233 NoTraceWord{}
234 OneTraceWord{first: U32}
235 TwoTraceWords{first: U32, second: U32}
236 ThreeTraceWords{first: U32, second: U32, third: U32}
237
238
239
240type SpanWords is Data:
241 NoSpanWord{}
242 OneSpanWord{first: U32}
243
244
245
246
247
248
249
250
251
252
253
254
255
256type TraceDraw is Data:
257 TraceDraw{remaining: Nat, words: TraceWords, excluded: Maybe<&2, TraceId>}
258
259
260
261type SpanDraw is Data:
262 SpanDraw{remaining: Nat, words: SpanWords, excluded: Maybe<&2, SpanId>}
263
264
265
266
267type TraceStep is Data:
268 NeedTraceWord{draw: TraceDraw}
269 DrawnTraceId{id: TraceId}
270 TraceFailed{error: GenerationError}
271
272
273
274type SpanStep is Data:
275 NeedSpanWord{draw: SpanDraw}
276 DrawnSpanId{id: SpanId}
277 SpanFailed{error: GenerationError}
278
279
280
281
282
283type SpanPlan is Data:
284 SpanPlan{excluded: Maybe<&2, SpanId>, trace_id: TraceId, sampled: Bool}
285
286
287
288
289
290
291
292
293
294type TracePlan is Data:
295 TracePlan{excluded: Maybe<&2, TraceId>, sampled: Bool}
296
297
298
299
300
301type Draw is Data:
302 DrawTrace{draw: TraceDraw, sampled: Bool}
303 DrawSpan{draw: SpanDraw, trace_id: TraceId, sampled: Bool}
304
305
306
307type Step is Data:
308 NeedWord{draw: Draw}
309 Created{context: LocalContext}
310 Failed{error: GenerationError}
311
312
313
314
315
316
317
318
319
320
321
322type Generation is Data:
323 Generation{fuel: Nat, step: Step}
324
325
326
327
328
329
330
331
332
333
334def Parsed.prepend(-n: Nat, digit: Hex.Digit, parsed: Parsed<n>) -> Parsed<1n+n>:
335 match parsed:
336 case Parsed{digits, rest}:
337 Parsed{Digits.DCon{digit, digits}, rest}
338
339
340
341def Parse.digit_result(value: Maybe<&2, Hex.Digit>, offset: Nat) ->
342 Result<&2, &2, Error, Hex.Digit>:
343 match value:
344 case None{}:
345 Fail{InvalidHex{offset}}
346 case Some{digit}:
347 Done{digit}
348
349
350def Parse.digit(char: Char, offset: Nat) -> Result<&2, &2, Error, Hex.Digit>:
351 Parse.digit_result(Hex.Digit.from_char(char), offset)
352
353
354
355
356def Parse.digits(n: Nat, text: String, offset: Nat) ->
357 Result<&2, &2, Error, Parsed<n>>:
358 match n:
359 case 0n:
360 Done{Parsed{Digits.DNil{}, text}}
361 case 1n+p:
362 match text:
363 case SNil{}:
364 Fail{UnexpectedEnd{offset}}
365 case SCon{char, tail}:
366 +at = offset
367 do Result<&2, &2, Error, Parsed<1n+p>>:
368 digit : Hex.Digit <- Parse.digit(char, at)
369 parsed : Parsed<p> <- Parse.digits(p, tail, 1n+at)
370 return Parsed.prepend(p, digit, parsed)
371
372
373def Parse.separator_result(tail: String, ok: Bool, offset: Nat) ->
374 Result<&2, &2, Error, String>:
375 match ok:
376 case False{}:
377 Fail{ExpectedSeparator{offset}}
378 case True{}:
379 Done{tail}
380
381
382def Parse.separator(text: String, offset: Nat) -> Result<&2, &2, Error, String>:
383 match text:
384 case SNil{}:
385 Fail{UnexpectedEnd{offset}}
386 case SCon{char, tail}:
387 Parse.separator_result(tail, Char.is_eq(char, '-'), offset)
388
389
390
391def Parse.end(text: String) -> Result<&2, &2, Error, Unit>:
392 match text:
393 case SNil{}:
394 Done{Unit{}}
395 case SCon{char, tail}:
396 Fail{TrailingInput{}}
397
398
399
400def Parse.version(digits: Digits.Digits(2n)) -> Result<&2, &2, Error, Unit>:
401 match digits:
402 case Digits.DCon{Hex.H0{}, Digits.DCon{Hex.H0{}, Digits.DNil{}}}:
403 Done{Unit{}}
404 case Digits.DCon{Hex.Hf{}, Digits.DCon{Hex.Hf{}, Digits.DNil{}}}:
405 Fail{ForbiddenVersion{}}
406 case other:
407 Fail{UnsupportedVersion{Digits.Digits.to_string(2n, other)}}
408
409
410
411def Parse.nonzero_result(-n: Nat, result: Maybe<&2, Digits.NonZero<n>>, field: Field) ->
412 Result<&2, &2, Error, Digits.NonZero<n>>:
413 match result:
414 case None{}:
415 Fail{ZeroId{field}}
416 case Some{id}:
417 Done{id}
418
419
420def Parse.nonzero(n: Nat, digits: Digits.Digits(n), field: Field) ->
421 Result<&2, &2, Error, Digits.NonZero<n>>:
422 Parse.nonzero_result(n, Digits.NonZero.new(n, digits), field)
423
424
425def Parse.flags(trace_id: Digits.NonZero<32n>, parent_id: Digits.NonZero<16n>,
426 parsed: Parsed<2n>) -> Result<&2, &2, Error, TraceParentV00>:
427 match parsed:
428 case Parsed{flags, rest}:
429 do Result<&2, &2, Error, TraceParentV00>:
430 end : Unit <- Parse.end(rest)
431 return TraceParentV00{trace_id, parent_id, flags}
432
433
434
435def Parse.parent(trace_id: Digits.NonZero<32n>, parsed: Parsed<16n>) ->
436 Result<&2, &2, Error, TraceParentV00>:
437 match parsed:
438 case Parsed{parent_digits, rest}:
439 do Result<&2, &2, Error, TraceParentV00>:
440 parent_id : Digits.NonZero<16n> <- Parse.nonzero(16n, parent_digits, ParentIdField{})
441 after : String <- Parse.separator(rest, 52n)
442 flags : Parsed<2n> <- Parse.digits(2n, after, 53n)
443 Parse.flags(trace_id, parent_id, flags)
444
445
446
447def Parse.trace(parsed: Parsed<32n>) -> Result<&2, &2, Error, TraceParentV00>:
448 match parsed:
449 case Parsed{trace_digits, rest}:
450 do Result<&2, &2, Error, TraceParentV00>:
451 trace_id : Digits.NonZero<32n> <- Parse.nonzero(32n, trace_digits, TraceIdField{})
452 after : String <- Parse.separator(rest, 35n)
453 parent : Parsed<16n> <- Parse.digits(16n, after, 36n)
454 Parse.parent(trace_id, parent)
455
456
457
458def Parse.start(parsed: Parsed<2n>) -> Result<&2, &2, Error, TraceParentV00>:
459 match parsed:
460 case Parsed{version, rest}:
461 do Result<&2, &2, Error, TraceParentV00>:
462 version_ok : Unit <- Parse.version(version)
463 after : String <- Parse.separator(rest, 2n)
464 trace : Parsed<32n> <- Parse.digits(32n, after, 3n)
465 Parse.trace(trace)
466
467
468
469
470
471
472
473
474
475def TraceParentV00.parse(text: String) -> Result<&2, &2, Error, TraceParentV00>:
476 do Result<&2, &2, Error, TraceParentV00>:
477 version : Parsed<2n> <- Parse.digits(2n, text, 0n)
478 Parse.start(version)
479
480
481
482
483
484def TraceParentV00.format(context: TraceParentV00) -> String:
485 match context:
486 case TraceParentV00{trace_id, parent_id, flags}:
487 "00-" ++ Digits.NonZero.to_string(32n, trace_id) ++ "-" ++
488 Digits.NonZero.to_string(16n, parent_id) ++ "-" ++
489 Digits.Digits.to_string(2n, flags)
490
491
492def TraceParentV00.is_sampled(context: TraceParentV00) -> Bool:
493 match context:
494 case TraceParentV00{trace_id, parent_id,
495 Digits.DCon{high, Digits.DCon{low, Digits.DNil{}}}}:
496 Hex.Digit.is_odd(low)
497
498
499
500def TraceParentV00.is_random(context: TraceParentV00) -> Bool:
501 match context:
502 case TraceParentV00{trace_id, parent_id,
503 Digits.DCon{high, Digits.DCon{low, Digits.DNil{}}}}:
504 Hex.Digit.has_bit1(low)
505
506
507
508
509
510
511
512
513
514
515
516
517def Parse.id.finish(n: Nat, parsed: Parsed<n>, field: Field) ->
518 Result<&2, &2, Error, Digits.NonZero<n>>:
519 match parsed:
520 case Parsed{digits, rest}:
521 do Result<&2, &2, Error, Digits.NonZero<n>>:
522 end : Unit <- Parse.end(rest)
523 Parse.nonzero(n, digits, field)
524
525
526def Parse.id(+n: Nat, text: String, field: Field) ->
527 Result<&2, &2, Error, Digits.NonZero<n>>:
528 do Result<&2, &2, Error, Digits.NonZero<n>>:
529 parsed : Parsed<n> <- Parse.digits(n, text, 0n)
530 Parse.id.finish(n, parsed, field)
531
532
533
534
535def TraceId.parse(text: String) -> Result<&2, &2, Error, TraceId>:
536 do Result<&2, &2, Error, TraceId>:
537 value : Digits.NonZero<32n> <- Parse.id(32n, text, TraceIdField{})
538 return TraceId{value, False{}}
539
540
541
542
543def TraceId.assert_random(id: TraceId) -> TraceId:
544 match id:
545 case TraceId{value, random}:
546 TraceId{value, True{}}
547
548
549def TraceId.is_random(id: TraceId) -> Bool:
550 match id:
551 case TraceId{value, random}:
552 random
553
554
555def TraceId.to_string(id: TraceId) -> String:
556 match id:
557 case TraceId{value, random}:
558 Digits.NonZero.to_string(32n, value)
559
560
561
562def TraceId.is_eq(a: TraceId, b: TraceId) -> Bool:
563 String.eq(TraceId.to_string(a), TraceId.to_string(b))
564
565
566
567def SpanId.parse(text: String) -> Result<&2, &2, Error, SpanId>:
568 do Result<&2, &2, Error, SpanId>:
569 value : Digits.NonZero<16n> <- Parse.id(16n, text, SpanIdField{})
570 return SpanId{value}
571
572
573def SpanId.to_string(id: SpanId) -> String:
574 match id:
575 case SpanId{value}:
576 Digits.NonZero.to_string(16n, value)
577
578
579def SpanId.is_eq(a: SpanId, b: SpanId) -> Bool:
580 String.eq(SpanId.to_string(a), SpanId.to_string(b))
581
582
583
584def Sampling.resolve(sampling: Sampling, inherited: Bool) -> Bool:
585 match sampling:
586 case InheritSampled{}:
587 inherited
588 case SetSampled{sampled}:
589 sampled
590
591
592
593
594def RemoteContext.from_traceparent(value: TraceParentV00) -> RemoteContext:
595 match value:
596 case TraceParentV00{trace_id, parent_id,
597 Digits.DCon{high, Digits.DCon{+low, Digits.DNil{}}}}:
598 RemoteContext{TraceId{trace_id, Hex.Digit.has_bit1(low)}, SpanId{parent_id},
599 Hex.Digit.is_odd(low)}
600
601
602
603
604
605
606
607
608
609def RemoteContext.from_ids(trace_id: TraceId, span_id: SpanId, sampled: Bool) -> RemoteContext:
610 RemoteContext{trace_id, span_id, sampled}
611
612
613def RemoteContext.trace_id(context: RemoteContext) -> TraceId:
614 match context:
615 case RemoteContext{trace_id, span_id, sampled}:
616 trace_id
617
618
619def RemoteContext.span_id(context: RemoteContext) -> SpanId:
620 match context:
621 case RemoteContext{trace_id, span_id, sampled}:
622 span_id
623
624
625def RemoteContext.is_sampled(context: RemoteContext) -> Bool:
626 match context:
627 case RemoteContext{trace_id, span_id, sampled}:
628 sampled
629
630
631
632def Flags.known(sampled: Bool, random: Bool) -> U32:
633 U32.or(Bool.to_u32(sampled), U32.shl(Bool.to_u32(random)))
634
635
636
637
638
639
640
641def RemoteContext.flags(context: RemoteContext) -> U32:
642 match context:
643 case RemoteContext{trace_id, span_id, sampled}:
644 Flags.known(sampled, TraceId.is_random(trace_id))
645
646
647def LocalContext.trace_id(context: LocalContext) -> TraceId:
648 match context:
649 case LocalContext{trace_id, span_id, sampled}:
650 trace_id
651
652
653def LocalContext.span_id(context: LocalContext) -> SpanId:
654 match context:
655 case LocalContext{trace_id, span_id, sampled}:
656 span_id
657
658
659def LocalContext.is_sampled(context: LocalContext) -> Bool:
660 match context:
661 case LocalContext{trace_id, span_id, sampled}:
662 sampled
663
664
665
666
667def LocalContext.to_traceparent(context: LocalContext) -> TraceParentV00:
668 match context:
669 case LocalContext{TraceId{trace_id, random}, SpanId{span_id}, sampled}:
670 TraceParentV00{trace_id, span_id,
671 Digits.DCon{Hex.H0{}, Digits.DCon{Hex.Digit.from_bits(sampled, random, False{}, False{}), Digits.DNil{}}}}
672
673
674
675
676
677
678
679def LocalContext.flags(context: LocalContext) -> U32:
680 match context:
681 case LocalContext{trace_id, span_id, sampled}:
682 Flags.known(sampled, TraceId.is_random(trace_id))
683
684
685def Parent.trace_id(parent: Parent) -> TraceId:
686 match parent:
687 case RemoteParent{context}:
688 RemoteContext.trace_id(context)
689 case LocalParent{context}:
690 LocalContext.trace_id(context)
691
692
693def Parent.span_id(parent: Parent) -> SpanId:
694 match parent:
695 case RemoteParent{context}:
696 RemoteContext.span_id(context)
697 case LocalParent{context}:
698 LocalContext.span_id(context)
699
700
701def Parent.is_sampled(parent: Parent) -> Bool:
702 match parent:
703 case RemoteParent{context}:
704 RemoteContext.is_sampled(context)
705 case LocalParent{context}:
706 LocalContext.is_sampled(context)
707
708
709
710
711def Context.from_ids(trace_id: TraceId, span_id: SpanId, sampled: Bool) -> LocalContext:
712 LocalContext{trace_id, span_id, sampled}
713
714
715
716
717
718
719
720def Context.root_from_ids(trace_id: TraceId, span_id: SpanId, sampled: Bool) -> LocalContext:
721 Context.from_ids(trace_id, span_id, sampled)
722
723
724def Context.child_from_id.checked(trace_id: TraceId, span_id: SpanId, sampled: Bool, reused: Bool) ->
725 Result<&2, &2, ContextError, LocalContext>:
726 match reused:
727 case True{}:
728 Fail{ReusedSpanId{}}
729 case False{}:
730 Done{Context.from_ids(trace_id, span_id, sampled)}
731
732
733
734
735
736def Context.child_from_id(+parent: Parent, +span_id: SpanId, sampling: Sampling) ->
737 Result<&2, &2, ContextError, LocalContext>:
738 Context.child_from_id.checked(Parent.trace_id(parent), span_id,
739 Sampling.resolve(sampling, Parent.is_sampled(parent)),
740 SpanId.is_eq(span_id, Parent.span_id(parent)))
741
742
743def Context.restart_from_ids.checked(trace_id: TraceId, span_id: SpanId, sampled: Bool, reused: Bool) ->
744 Result<&2, &2, ContextError, LocalContext>:
745 match reused:
746 case True{}:
747 Fail{ReusedTraceId{}}
748 case False{}:
749 Done{Context.root_from_ids(trace_id, span_id, sampled)}
750
751
752
753
754
755
756def Context.restart_from_ids(previous: RemoteContext, +trace_id: TraceId, span_id: SpanId, sampled: Bool) ->
757 Result<&2, &2, ContextError, LocalContext>:
758 Context.restart_from_ids.checked(trace_id, span_id, sampled,
759 TraceId.is_eq(trace_id, RemoteContext.trace_id(previous)))
760
761
762
763
764
765
766
767
768
769
770def U32.to_hex(word: U32) -> String:
771 Digits.Digits.to_string(8n, Digits.Digits.of_u32(word))
772
773
774def TraceId.from_digits(value: Maybe<&2, Digits.NonZero<32n>>) -> Maybe<&2, TraceId>:
775 match value:
776 case None{}:
777 None{}
778 case Some{digits}:
779 Some{TraceId{digits, False{}}}
780
781
782
783
784
785def TraceId.from_words(first: U32, second: U32, third: U32, fourth: U32) -> Maybe<&2, TraceId>:
786 TraceId.from_digits(Digits.NonZero.new(32n,
787 Digits.Digits.append(8n, 24n, Digits.Digits.of_u32(first),
788 Digits.Digits.append(8n, 16n, Digits.Digits.of_u32(second),
789 Digits.Digits.append(8n, 8n, Digits.Digits.of_u32(third), Digits.Digits.of_u32(fourth))))))
790
791
792def SpanId.from_digits(value: Maybe<&2, Digits.NonZero<16n>>) -> Maybe<&2, SpanId>:
793 match value:
794 case None{}:
795 None{}
796 case Some{digits}:
797 Some{SpanId{digits}}
798
799
800
801def SpanId.from_words(first: U32, second: U32) -> Maybe<&2, SpanId>:
802 SpanId.from_digits(Digits.NonZero.new(16n,
803 Digits.Digits.append(8n, 8n, Digits.Digits.of_u32(first), Digits.Digits.of_u32(second))))
804
805
806
807
808
809
810
811
812
813
814
815def ParsedBytes.prepend(-n: Nat, digits: Digits.Digits(2n), parsed: ParsedBytes<n>) -> ParsedBytes<1n+n>:
816 match digits parsed:
817 case Digits.DCon{high, Digits.DCon{low, Digits.DNil{}}} ParsedBytes{rest_digits, rest}:
818 ParsedBytes{Digits.DCon{high, Digits.DCon{low, rest_digits}}, rest}
819
820
821
822def Parse.byte.checked(cell: U32, above: Bool, offset: Nat) -> Result<&2, &2, Error, Digits.Digits(2n)>:
823 match above:
824 case True{}:
825 Fail{InvalidByte{offset}}
826 case False{}:
827 Done{Digits.Digits.of_byte(cell)}
828
829
830def Parse.byte(+cell: U32, offset: Nat) -> Result<&2, &2, Error, Digits.Digits(2n)>:
831 Parse.byte.checked(cell, U32.is_gt(cell, 255), offset)
832
833
834
835
836def Parse.bytes(n: Nat, bytes: List<&2, U32>, offset: Nat) -> Result<&2, &2, Error, ParsedBytes<n>>:
837 match n:
838 case 0n:
839 Done{ParsedBytes{Digits.DNil{}, bytes}}
840 case 1n+p:
841 match bytes:
842 case Nil{}:
843 Fail{UnexpectedEnd{offset}}
844 case Con{cell, tail}:
845 +at = offset
846 do Result<&2, &2, Error, ParsedBytes<1n+p>>:
847 digits : Digits.Digits(2n) <- Parse.byte(cell, at)
848 parsed : ParsedBytes<p> <- Parse.bytes(p, tail, 1n+at)
849 return ParsedBytes.prepend(p, digits, parsed)
850
851
852
853def Parse.bytes_end(bytes: List<&2, U32>) -> Result<&2, &2, Error, Unit>:
854 match bytes:
855 case Nil{}:
856 Done{Unit{}}
857 case Con{cell, rest}:
858 Fail{TrailingInput{}}
859
860
861def Parse.id_bytes.finish(n: Nat, parsed: ParsedBytes<n>, field: Field) ->
862 Result<&2, &2, Error, Digits.NonZero<Nat.double(n)>>:
863 match parsed:
864 case ParsedBytes{digits, rest}:
865 do Result<&2, &2, Error, Digits.NonZero<Nat.double(n)>>:
866 end : Unit <- Parse.bytes_end(rest)
867 Parse.nonzero(Nat.double(n), digits, field)
868
869
870def Parse.id_bytes(+n: Nat, bytes: List<&2, U32>, field: Field) ->
871 Result<&2, &2, Error, Digits.NonZero<Nat.double(n)>>:
872 do Result<&2, &2, Error, Digits.NonZero<Nat.double(n)>>:
873 parsed : ParsedBytes<n> <- Parse.bytes(n, bytes, 0n)
874 Parse.id_bytes.finish(n, parsed, field)
875
876
877
878
879
880
881def TraceId.to_bytes(id: TraceId) -> List<&2, U32>:
882 match id:
883 case TraceId{value, random}:
884 Digits.Digits.to_bytes(16n, Digits.NonZero.digits(32n, value))
885
886
887
888
889
890
891
892
893
894
895def TraceId.from_bytes(bytes: List<&2, U32>) -> Result<&2, &2, Error, TraceId>:
896 do Result<&2, &2, Error, TraceId>:
897 value : Digits.NonZero<32n> <- Parse.id_bytes(16n, bytes, TraceIdField{})
898 return TraceId{value, False{}}
899
900
901
902def SpanId.to_bytes(id: SpanId) -> List<&2, U32>:
903 match id:
904 case SpanId{value}:
905 Digits.Digits.to_bytes(8n, Digits.NonZero.digits(16n, value))
906
907
908
909
910
911def SpanId.from_bytes(bytes: List<&2, U32>) -> Result<&2, &2, Error, SpanId>:
912 do Result<&2, &2, Error, SpanId>:
913 value : Digits.NonZero<16n> <- Parse.id_bytes(8n, bytes, SpanIdField{})
914 return SpanId{value}
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936def Draw.asserted(candidate: Maybe<&2, TraceId>) -> Maybe<&2, TraceId>:
937 match candidate:
938 case None{}:
939 None{}
940 case Some{id}:
941 Some{TraceId.assert_random(id)}
942
943
944
945def Tape.next(tape: List<&1, Result<&1, &1, U32 & String, U32>>) ->
946 List<&1, Result<&1, &1, U32 & String, U32>> & Result<&1, &1, U32 & String, U32>:
947 match tape:
948 case Nil{}:
949 (Nil{}, Fail{(1, "tape-exhausted")})
950 case Con{word, rest}:
951 (rest, word)
952
953
954
955
956
957
958
959
960
961def Drive.fed(~T: Data, ~D: Data, ~waiting: T -> Maybe<&2, D>, ~feed: Result<&1, &1, U32 & String, U32> -> D -> T,
962 -S: Type, got: S & Result<&1, &1, U32 & String, U32>, draw: D) -> S & (T & Maybe<&2, D>):
963 (state, word) = got
964 +next = feed(word, draw)
965 (state, (next, waiting(next)))
966
967
968
969
970def Drive.run(~T: Data, ~D: Data, ~waiting: T -> Maybe<&2, D>, ~feed: Result<&1, &1, U32 & String, U32> -> D -> T,
971 fuel: Nat, current: List<&1, Result<&1, &1, U32 & String, U32>> & (T & Maybe<&2, D>)) ->
972 List<&1, Result<&1, &1, U32 & String, U32>> & T:
973 match fuel current:
974 case _ Tuple{tape, Tuple{step, None{}}}:
975 (tape, step)
976 case 0n Tuple{tape, Tuple{step, Some{draw}}}:
977 (tape, step)
978 case 1n+p Tuple{tape, Tuple{step, Some{draw}}}:
979 Drive.run(~T, ~D, ~waiting, ~feed, p, Drive.fed(~T, ~D, ~waiting, ~feed,
980 List<&1, Result<&1, &1, U32 & String, U32>>, Tape.next(tape), draw))
981
982
983
984
985
986
987
988def Drive.read(~T: Data, ~D: Data, ~waiting: T -> Maybe<&2, D>, ~feed: Result<&1, &1, U32 & String, U32> -> D -> T,
989 ~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), fuel: Nat, current: S & (T & Maybe<&2, D>)) ->
990 IO(S & T):
991 match fuel current:
992 case _ Tuple{state, Tuple{step, None{}}}:
993 IO.pure(S & T, (state, step))
994 case 0n Tuple{state, Tuple{step, Some{draw}}}:
995 IO.pure(S & T, (state, step))
996 case 1n+p Tuple{state, Tuple{step, Some{draw}}}:
997 IO.bind(S & Result<&1, &1, U32 & String, U32>, S & T, read(state),
998 got => Drive.read(~T, ~D, ~waiting, ~feed, ~S, ~read, p, Drive.fed(~T, ~D, ~waiting, ~feed, S, got, draw)))
999
1000
1001def Drive.ended(~T: Data, ~D: Data, ~waiting: T -> Maybe<&2, D>,
1002 result: List<&1, Result<&1, &1, U32 & String, U32>> & T) -> Bool:
1003 match result:
1004 case Tuple{tape, step}:
1005 Maybe.is_none(&2, D, waiting(step))
1006
1007
1008
1009def Drive.outcome(~T: Data, ~A: Data, ~result: T -> Result<&2, &2, GenerationError, A>, -S: Type, done: S & T) ->
1010 S & Result<&2, &2, GenerationError, A>:
1011 (state, step) = done
1012 (state, result(step))
1013
1014
1015def Drive.finished(~T: Data, ~A: Data, ~result: T -> Result<&2, &2, GenerationError, A>, ~S: Type,
1016 action: IO(S & T)) -> IO(S & Result<&2, &2, GenerationError, A>):
1017 IO.bind(S & T, S & Result<&2, &2, GenerationError, A>, action,
1018 done => IO.pure(S & Result<&2, &2, GenerationError, A>, Drive.outcome(~T, ~A, ~result, S, done)))
1019
1020
1021
1022
1023
1024
1025def TraceDraw.start(excluded: Maybe<&2, TraceId>) -> TraceStep:
1026 NeedTraceWord{TraceDraw{7n, NoTraceWord{}, excluded}}
1027
1028
1029def TraceDraw.retry(remaining: Nat, excluded: Maybe<&2, TraceId>) -> TraceStep:
1030 match remaining:
1031 case 0n:
1032 TraceFailed{ExhaustedTraceId{}}
1033 case 1n+p:
1034 NeedTraceWord{TraceDraw{p, NoTraceWord{}, excluded}}
1035
1036
1037def TraceDraw.reuse(reused: Bool, id: TraceId, excluded: TraceId, remaining: Nat) -> TraceStep:
1038 match reused:
1039 case True{}:
1040 TraceDraw.retry(remaining, Some{excluded})
1041 case False{}:
1042 DrawnTraceId{id}
1043
1044
1045
1046def TraceDraw.check(candidate: Maybe<&2, TraceId>, excluded: Maybe<&2, TraceId>, remaining: Nat) -> TraceStep:
1047 match candidate:
1048 case None{}:
1049 TraceDraw.retry(remaining, excluded)
1050 case Some{+id}:
1051 match excluded:
1052 case None{}:
1053 DrawnTraceId{id}
1054 case Some{+old}:
1055 TraceDraw.reuse(TraceId.is_eq(id, old), id, old, remaining)
1056
1057
1058
1059def TraceDraw.take(word: U32, remaining: Nat, words: TraceWords, excluded: Maybe<&2, TraceId>) -> TraceStep:
1060 match words:
1061 case NoTraceWord{}:
1062 NeedTraceWord{TraceDraw{remaining, OneTraceWord{word}, excluded}}
1063 case OneTraceWord{first}:
1064 NeedTraceWord{TraceDraw{remaining, TwoTraceWords{first, word}, excluded}}
1065 case TwoTraceWords{first, second}:
1066 NeedTraceWord{TraceDraw{remaining, ThreeTraceWords{first, second, word}, excluded}}
1067 case ThreeTraceWords{first, second, third}:
1068 TraceDraw.check(Draw.asserted(TraceId.from_words(first, second, third, word)), excluded, remaining)
1069
1070
1071
1072
1073def TraceDraw.feed(word: Result<&1, &1, U32 & String, U32>, remaining: Nat, words: TraceWords,
1074 excluded: Maybe<&2, TraceId>) -> TraceStep:
1075 match word:
1076 case Fail{(code, message)}:
1077 TraceFailed{SourceFailure{code, message}}
1078 case Done{next}:
1079 TraceDraw.take(next, remaining, words, excluded)
1080
1081
1082
1083def TraceDraw.next(word: Result<&1, &1, U32 & String, U32>, draw: TraceDraw) -> TraceStep:
1084 match draw:
1085 case TraceDraw{remaining, words, excluded}:
1086 TraceDraw.feed(word, remaining, words, excluded)
1087
1088
1089
1090def TraceStep.waiting(step: TraceStep) -> Maybe<&2, TraceDraw>:
1091 match step:
1092 case NeedTraceWord{draw}:
1093 Some{draw}
1094 case DrawnTraceId{id}:
1095 None{}
1096 case TraceFailed{error}:
1097 None{}
1098
1099
1100
1101
1102def TraceDraw.result(step: TraceStep) -> Result<&2, &2, GenerationError, TraceId>:
1103 match step:
1104 case DrawnTraceId{id}:
1105 Done{id}
1106 case TraceFailed{error}:
1107 Fail{error}
1108 case NeedTraceWord{draw}:
1109 Fail{ExhaustedTraceId{}}
1110
1111
1112def TraceDraw.run(fuel: Nat, current: List<&1, Result<&1, &1, U32 & String, U32>> & TraceStep) ->
1113 List<&1, Result<&1, &1, U32 & String, U32>> & TraceStep:
1114 match current:
1115 case Tuple{tape, +step}:
1116 Drive.run(~TraceStep, ~TraceDraw, ~TraceStep.waiting, ~TraceDraw.next, fuel,
1117 (tape, (step, TraceStep.waiting(step))))
1118
1119
1120def TraceDraw.ended(result: List<&1, Result<&1, &1, U32 & String, U32>> & TraceStep) -> Bool:
1121 Drive.ended(~TraceStep, ~TraceDraw, ~TraceStep.waiting, result)
1122
1123
1124def TraceDraw.outcome(-S: Type, done: S & TraceStep) -> S & Result<&2, &2, GenerationError, TraceId>:
1125 Drive.outcome(~TraceStep, ~TraceId, ~TraceDraw.result, S, done)
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136def TraceId.generate_with(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), source: S,
1137 excluded: Maybe<&2, TraceId>) -> IO(S & Result<&2, &2, GenerationError, TraceId>):
1138 +start = TraceDraw.start(excluded)
1139 Drive.finished(~TraceStep, ~TraceId, ~TraceDraw.result, ~S, Drive.read(~TraceStep, ~TraceDraw, ~TraceStep.waiting,
1140 ~TraceDraw.next, ~S, ~read, 32n, (source, (start, TraceStep.waiting(start)))))
1141
1142
1143
1144
1145
1146
1147def SpanDraw.start(excluded: Maybe<&2, SpanId>) -> SpanStep:
1148 NeedSpanWord{SpanDraw{7n, NoSpanWord{}, excluded}}
1149
1150
1151def SpanDraw.retry(remaining: Nat, excluded: Maybe<&2, SpanId>) -> SpanStep:
1152 match remaining:
1153 case 0n:
1154 SpanFailed{ExhaustedSpanId{}}
1155 case 1n+p:
1156 NeedSpanWord{SpanDraw{p, NoSpanWord{}, excluded}}
1157
1158
1159def SpanDraw.reuse(reused: Bool, id: SpanId, excluded: SpanId, remaining: Nat) -> SpanStep:
1160 match reused:
1161 case True{}:
1162 SpanDraw.retry(remaining, Some{excluded})
1163 case False{}:
1164 DrawnSpanId{id}
1165
1166
1167
1168def SpanDraw.check(candidate: Maybe<&2, SpanId>, excluded: Maybe<&2, SpanId>, remaining: Nat) -> SpanStep:
1169 match candidate:
1170 case None{}:
1171 SpanDraw.retry(remaining, excluded)
1172 case Some{+id}:
1173 match excluded:
1174 case None{}:
1175 DrawnSpanId{id}
1176 case Some{+old}:
1177 SpanDraw.reuse(SpanId.is_eq(id, old), id, old, remaining)
1178
1179
1180
1181def SpanDraw.take(word: U32, remaining: Nat, words: SpanWords, excluded: Maybe<&2, SpanId>) -> SpanStep:
1182 match words:
1183 case NoSpanWord{}:
1184 NeedSpanWord{SpanDraw{remaining, OneSpanWord{word}, excluded}}
1185 case OneSpanWord{first}:
1186 SpanDraw.check(SpanId.from_words(first, word), excluded, remaining)
1187
1188
1189
1190
1191def SpanDraw.feed(word: Result<&1, &1, U32 & String, U32>, remaining: Nat, words: SpanWords,
1192 excluded: Maybe<&2, SpanId>) -> SpanStep:
1193 match word:
1194 case Fail{(code, message)}:
1195 SpanFailed{SourceFailure{code, message}}
1196 case Done{next}:
1197 SpanDraw.take(next, remaining, words, excluded)
1198
1199
1200
1201def SpanDraw.next(word: Result<&1, &1, U32 & String, U32>, draw: SpanDraw) -> SpanStep:
1202 match draw:
1203 case SpanDraw{remaining, words, excluded}:
1204 SpanDraw.feed(word, remaining, words, excluded)
1205
1206
1207
1208def SpanStep.waiting(step: SpanStep) -> Maybe<&2, SpanDraw>:
1209 match step:
1210 case NeedSpanWord{draw}:
1211 Some{draw}
1212 case DrawnSpanId{id}:
1213 None{}
1214 case SpanFailed{error}:
1215 None{}
1216
1217
1218
1219
1220def SpanDraw.result(step: SpanStep) -> Result<&2, &2, GenerationError, SpanId>:
1221 match step:
1222 case DrawnSpanId{id}:
1223 Done{id}
1224 case SpanFailed{error}:
1225 Fail{error}
1226 case NeedSpanWord{draw}:
1227 Fail{ExhaustedSpanId{}}
1228
1229
1230def SpanDraw.run(fuel: Nat, current: List<&1, Result<&1, &1, U32 & String, U32>> & SpanStep) ->
1231 List<&1, Result<&1, &1, U32 & String, U32>> & SpanStep:
1232 match current:
1233 case Tuple{tape, +step}:
1234 Drive.run(~SpanStep, ~SpanDraw, ~SpanStep.waiting, ~SpanDraw.next, fuel,
1235 (tape, (step, SpanStep.waiting(step))))
1236
1237
1238def SpanDraw.ended(result: List<&1, Result<&1, &1, U32 & String, U32>> & SpanStep) -> Bool:
1239 Drive.ended(~SpanStep, ~SpanDraw, ~SpanStep.waiting, result)
1240
1241
1242def SpanDraw.outcome(-S: Type, done: S & SpanStep) -> S & Result<&2, &2, GenerationError, SpanId>:
1243 Drive.outcome(~SpanStep, ~SpanId, ~SpanDraw.result, S, done)
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253def SpanId.generate_with(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), source: S,
1254 excluded: Maybe<&2, SpanId>) -> IO(S & Result<&2, &2, GenerationError, SpanId>):
1255 +start = SpanDraw.start(excluded)
1256 Drive.finished(~SpanStep, ~SpanId, ~SpanDraw.result, ~S, Drive.read(~SpanStep, ~SpanDraw, ~SpanStep.waiting,
1257 ~SpanDraw.next, ~S, ~read, 16n, (source, (start, SpanStep.waiting(start)))))
1258
1259
1260
1261
1262
1263
1264
1265def Step.of_span(step: SpanStep, trace_id: TraceId, sampled: Bool) -> Step:
1266 match step:
1267 case NeedSpanWord{draw}:
1268 NeedWord{DrawSpan{draw, trace_id, sampled}}
1269 case DrawnSpanId{id}:
1270 Created{Context.from_ids(trace_id, id, sampled)}
1271 case SpanFailed{error}:
1272 Failed{error}
1273
1274
1275
1276
1277def SpanPlan.root(trace_id: TraceId, sampled: Bool) -> SpanPlan:
1278 SpanPlan{None{}, trace_id, sampled}
1279
1280
1281
1282
1283def SpanPlan.child(+parent: Parent, sampling: Sampling) -> SpanPlan:
1284 SpanPlan{Some{Parent.span_id(parent)}, Parent.trace_id(parent), Sampling.resolve(sampling, Parent.is_sampled(parent))}
1285
1286
1287def SpanPlan.draw(plan: SpanPlan) -> Step:
1288 match plan:
1289 case SpanPlan{excluded, trace_id, sampled}:
1290 Step.of_span(SpanDraw.start(excluded), trace_id, sampled)
1291
1292
1293
1294
1295
1296def Step.of_trace(step: TraceStep, sampled: Bool) -> Step:
1297 match step:
1298 case NeedTraceWord{draw}:
1299 NeedWord{DrawTrace{draw, sampled}}
1300 case DrawnTraceId{id}:
1301 SpanPlan.draw(SpanPlan.root(id, sampled))
1302 case TraceFailed{error}:
1303 Failed{error}
1304
1305
1306
1307def TracePlan.root(sampled: Bool) -> TracePlan:
1308 TracePlan{None{}, sampled}
1309
1310
1311
1312def TracePlan.restart(previous: RemoteContext, sampled: Bool) -> TracePlan:
1313 TracePlan{Some{RemoteContext.trace_id(previous)}, sampled}
1314
1315
1316
1317def TracePlan.draw(plan: TracePlan) -> Step:
1318 match plan:
1319 case TracePlan{excluded, sampled}:
1320 Step.of_trace(TraceDraw.start(excluded), sampled)
1321
1322
1323def Draw.root(sampled: Bool) -> Step:
1324 TracePlan.draw(TracePlan.root(sampled))
1325
1326
1327
1328def Draw.restart(previous: RemoteContext, sampled: Bool) -> Step:
1329 TracePlan.draw(TracePlan.restart(previous, sampled))
1330
1331
1332def Draw.child(+parent: Parent, sampling: Sampling) -> Step:
1333 SpanPlan.draw(SpanPlan.child(parent, sampling))
1334
1335
1336
1337
1338def Draw.feed(word: Result<&1, &1, U32 & String, U32>, draw: Draw) -> Step:
1339 match draw:
1340 case DrawTrace{trace, sampled}:
1341 Step.of_trace(TraceDraw.next(word, trace), sampled)
1342 case DrawSpan{span, trace_id, sampled}:
1343 Step.of_span(SpanDraw.next(word, span), trace_id, sampled)
1344
1345
1346
1347def Step.waiting(step: Step) -> Maybe<&2, Draw>:
1348 match step:
1349 case NeedWord{draw}:
1350 Some{draw}
1351 case Created{context}:
1352 None{}
1353 case Failed{error}:
1354 None{}
1355
1356
1357
1358
1359
1360def Step.result(step: Step) -> Result<&2, &2, GenerationError, LocalContext>:
1361 match step:
1362 case Created{context}:
1363 Done{context}
1364 case Failed{error}:
1365 Fail{error}
1366 case NeedWord{DrawTrace{draw, sampled}}:
1367 Fail{ExhaustedTraceId{}}
1368 case NeedWord{DrawSpan{draw, trace_id, sampled}}:
1369 Fail{ExhaustedSpanId{}}
1370
1371
1372def Draw.run(fuel: Nat, current: List<&1, Result<&1, &1, U32 & String, U32>> & Step) ->
1373 List<&1, Result<&1, &1, U32 & String, U32>> & Step:
1374 match current:
1375 case Tuple{tape, +step}:
1376 Drive.run(~Step, ~Draw, ~Step.waiting, ~Draw.feed, fuel, (tape, (step, Step.waiting(step))))
1377
1378
1379def Draw.read(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), fuel: Nat,
1380 current: S & Step) -> IO(S & Step):
1381 match current:
1382 case Tuple{state, +step}:
1383 Drive.read(~Step, ~Draw, ~Step.waiting, ~Draw.feed, ~S, ~read, fuel, (state, (step, Step.waiting(step))))
1384
1385
1386def Draw.ended(result: List<&1, Result<&1, &1, U32 & String, U32>> & Step) -> Bool:
1387 Drive.ended(~Step, ~Draw, ~Step.waiting, result)
1388
1389
1390def Draw.outcome(-S: Type, done: S & Step) -> S & Result<&2, &2, GenerationError, LocalContext>:
1391 Drive.outcome(~Step, ~LocalContext, ~Step.result, S, done)
1392
1393
1394def Draw.finished(~S: Type, action: IO(S & Step)) -> IO(S & Result<&2, &2, GenerationError, LocalContext>):
1395 Drive.finished(~Step, ~LocalContext, ~Step.result, ~S, action)
1396
1397
1398
1399def TracePlan.generation(plan: TracePlan) -> Generation:
1400 Generation{48n, TracePlan.draw(plan)}
1401
1402
1403
1404def Generation.root(sampled: Bool) -> Generation:
1405 TracePlan.generation(TracePlan.root(sampled))
1406
1407
1408
1409def Generation.restart(previous: RemoteContext, sampled: Bool) -> Generation:
1410 TracePlan.generation(TracePlan.restart(previous, sampled))
1411
1412
1413def Generation.child(+parent: Parent, sampling: Sampling) -> Generation:
1414 Generation{16n, Draw.child(parent, sampling)}
1415
1416
1417def Generation.needs.of(fuel: Nat, step: Step) -> Bool:
1418 match fuel step:
1419 case 0n _:
1420 False{}
1421 case 1n+p NeedWord{draw}:
1422 True{}
1423 case 1n+p Created{context}:
1424 False{}
1425 case 1n+p Failed{error}:
1426 False{}
1427
1428
1429
1430def Generation.needs(generation: Generation) -> Bool:
1431 match generation:
1432 case Generation{fuel, step}:
1433 Generation.needs.of(fuel, step)
1434
1435
1436
1437def Generation.fed(fuel: Nat, step: Step, word: Result<&1, &1, U32 & String, U32>) -> Generation:
1438 match fuel step:
1439 case 0n other:
1440 Generation{0n, other}
1441 case 1n+p NeedWord{draw}:
1442 Generation{p, Draw.feed(word, draw)}
1443 case 1n+p Created{context}:
1444 Generation{0n, Created{context}}
1445 case 1n+p Failed{error}:
1446 Generation{0n, Failed{error}}
1447
1448
1449
1450def Generation.feed(generation: Generation, word: Result<&1, &1, U32 & String, U32>) -> Generation:
1451 match generation:
1452 case Generation{fuel, step}:
1453 Generation.fed(fuel, step, word)
1454
1455
1456
1457
1458
1459def Generation.result(generation: Generation) -> Result<&2, &2, GenerationError, LocalContext>:
1460 match generation:
1461 case Generation{fuel, step}:
1462 Step.result(step)
1463
1464
1465
1466def Generation.run_with(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), source: S,
1467 generation: Generation) -> IO(S & Result<&2, &2, GenerationError, LocalContext>):
1468 match generation:
1469 case Generation{fuel, step}:
1470 Draw.finished(~S, Draw.read(~S, ~read, fuel, (source, step)))
1471
1472
1473
1474
1475def Draw.span_context(-S: Type, +trace_id: TraceId, +sampled: Bool,
1476 done: S & Result<&2, &2, GenerationError, SpanId>) -> S & Result<&2, &2, GenerationError, LocalContext>:
1477 match done:
1478 case Tuple{state, Fail{error}}:
1479 (state, Fail{error})
1480 case Tuple{state, Done{span_id}}:
1481 (state, Done{Context.from_ids(trace_id, span_id, sampled)})
1482
1483
1484
1485
1486def SpanPlan.generate_with(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), source: S,
1487 plan: SpanPlan) -> IO(S & Result<&2, &2, GenerationError, LocalContext>):
1488 match plan:
1489 case SpanPlan{excluded, trace_id, sampled}:
1490 IO.bind(S & Result<&2, &2, GenerationError, SpanId>, S & Result<&2, &2, GenerationError, LocalContext>,
1491 SpanId.generate_with(~S, ~read, source, excluded),
1492 outcome => IO.pure(S & Result<&2, &2, GenerationError, LocalContext>,
1493 Draw.span_context(S, trace_id, sampled, outcome)))
1494
1495
1496
1497
1498
1499def Draw.root_span_with(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), sampled: Bool,
1500 outcome: S & Result<&2, &2, GenerationError, TraceId>) -> IO(S & Result<&2, &2, GenerationError, LocalContext>):
1501 match outcome:
1502 case Tuple{state, Fail{error}}:
1503 IO.pure(S & Result<&2, &2, GenerationError, LocalContext>, (state, Fail{error}))
1504 case Tuple{state, Done{trace_id}}:
1505 SpanPlan.generate_with(~S, ~read, state, SpanPlan.root(trace_id, sampled))
1506
1507
1508
1509
1510
1511def TracePlan.generate_with(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), source: S,
1512 plan: TracePlan) -> IO(S & Result<&2, &2, GenerationError, LocalContext>):
1513 match plan:
1514 case TracePlan{excluded, sampled}:
1515 IO.bind(S & Result<&2, &2, GenerationError, TraceId>, S & Result<&2, &2, GenerationError, LocalContext>,
1516 TraceId.generate_with(~S, ~read, source, excluded), outcome => Draw.root_span_with(~S, ~read, sampled, outcome))
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528def Context.root_with(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), source: S, sampled: Bool) ->
1529 IO(S & Result<&2, &2, GenerationError, LocalContext>):
1530 TracePlan.generate_with(~S, ~read, source, TracePlan.root(sampled))
1531
1532
1533
1534
1535
1536
1537def Context.child_with(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), source: S,
1538 parent: Parent, sampling: Sampling) -> IO(S & Result<&2, &2, GenerationError, LocalContext>):
1539 SpanPlan.generate_with(~S, ~read, source, SpanPlan.child(parent, sampling))
1540
1541
1542
1543
1544
1545
1546def Context.restart_with(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), source: S,
1547 previous: RemoteContext, sampled: Bool) -> IO(S & Result<&2, &2, GenerationError, LocalContext>):
1548 TracePlan.generate_with(~S, ~read, source, TracePlan.restart(previous, sampled))
1549
1550
1551def Source.tape(tape: List<&1, Result<&1, &1, U32 & String, U32>>) ->
1552 IO(List<&1, Result<&1, &1, U32 & String, U32>> & Result<&1, &1, U32 & String, U32>):
1553 IO.pure(List<&1, Result<&1, &1, U32 & String, U32>> & Result<&1, &1, U32 & String, U32>, Tape.next(tape))
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565def Error.show(error: Error) -> String:
1566 match error:
1567 case UnexpectedEnd{offset}:
1568 "UnexpectedEnd at " ++ Nat.show(offset)
1569 case InvalidHex{offset}:
1570 "InvalidHex at " ++ Nat.show(offset)
1571 case InvalidByte{offset}:
1572 "InvalidByte at " ++ Nat.show(offset)
1573 case ExpectedSeparator{offset}:
1574 "ExpectedSeparator at " ++ Nat.show(offset)
1575 case TrailingInput{}:
1576 "TrailingInput"
1577 case ForbiddenVersion{}:
1578 "ForbiddenVersion"
1579 case UnsupportedVersion{version}:
1580 "UnsupportedVersion " ++ version
1581 case ZeroId{TraceIdField{}}:
1582 "ZeroTraceId"
1583 case ZeroId{ParentIdField{}}:
1584 "ZeroParentId"
1585 case ZeroId{SpanIdField{}}:
1586 "ZeroSpanId"
1587 case ControlCharacter{offset}:
1588 "ControlCharacter at " ++ Nat.show(offset)
1589
1590
1591def ContextError.show(error: ContextError) -> String:
1592 match error:
1593 case ReusedSpanId{}:
1594 "ReusedSpanId"
1595 case ReusedTraceId{}:
1596 "ReusedTraceId"
1597
1598
1599
1600def GenerationError.show(error: GenerationError) -> String:
1601 match error:
1602 case SourceFailure{code, message}:
1603 "SourceFailure " ++ U32.show(code) ++ " " ++ message
1604 case ExhaustedTraceId{}:
1605 "ExhaustedTraceId"
1606 case ExhaustedSpanId{}:
1607 "ExhaustedSpanId"
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621type LimitsError is Data:
1622 TraceParentInputTooSmall{}
1623 TraceStateOutputTooSmall{}
1624 TraceStateInputTooSmall{}
1625
1626
1627
1628
1629
1630
1631def Limits.is_valid(traceparent_input: Nat, tracestate_input: Nat, +tracestate_output: Nat) -> Bool:
1632 Bool.and(Nat.is_ge(traceparent_input, 55n),
1633 Bool.and(Nat.is_ge(tracestate_output, 512n), Nat.is_ge(tracestate_input, tracestate_output)))
1634
1635
1636
1637
1638
1639type Limits is Data:
1640 Limits{
1641 traceparent_input: Nat,
1642 tracestate_input: Nat,
1643 tracestate_output: Nat,
1644 evidence: {Limits.is_valid(traceparent_input, tracestate_input, tracestate_output) == True{} : Bool}
1645 }
1646
1647
1648def Limits.error(traceparent_input: Nat, tracestate_output: Nat) -> LimitsError:
1649 Bool.pick(LimitsError, Nat.is_lt(traceparent_input, 55n), TraceParentInputTooSmall{},
1650 Bool.pick(LimitsError, Nat.is_lt(tracestate_output, 512n), TraceStateOutputTooSmall{},
1651 TraceStateInputTooSmall{}))
1652
1653
1654def Limits.new.checked(traceparent_input: Nat, tracestate_input: Nat, tracestate_output: Nat, valid: Bool,
1655 evidence: {Limits.is_valid(traceparent_input, tracestate_input, tracestate_output) == valid : Bool}) ->
1656 Result<&2, &2, LimitsError, Limits>:
1657 match valid:
1658 case True{}:
1659 Done{Limits{traceparent_input, tracestate_input, tracestate_output, evidence}}
1660 case False{}:
1661 Fail{Limits.error(traceparent_input, tracestate_output)}
1662
1663
1664
1665def Limits.new(+traceparent_input: Nat, +tracestate_input: Nat, +tracestate_output: Nat) ->
1666 Result<&2, &2, LimitsError, Limits>:
1667 Limits.new.checked(traceparent_input, tracestate_input, tracestate_output,
1668 Limits.is_valid(traceparent_input, tracestate_input, tracestate_output), {==})
1669
1670
1671
1672
1673def Limits.default() -> Limits:
1674 Limits{32768n, 32768n, 512n, {==}}
1675
1676
1677def Limits.traceparent_input(limits: Limits) -> Nat:
1678 match limits:
1679 case Limits{traceparent_input, tracestate_input, tracestate_output, evidence}:
1680 traceparent_input
1681
1682
1683
1684def Limits.tracestate_input(limits: Limits) -> Nat:
1685 match limits:
1686 case Limits{traceparent_input, tracestate_input, tracestate_output, evidence}:
1687 tracestate_input
1688
1689
1690def Limits.tracestate_output(limits: Limits) -> Nat:
1691 match limits:
1692 case Limits{traceparent_input, tracestate_input, tracestate_output, evidence}:
1693 tracestate_output
1694
1695
1696def LimitsError.show(error: LimitsError) -> String:
1697 match error:
1698 case TraceParentInputTooSmall{}:
1699 "TraceParentInputTooSmall"
1700 case TraceStateOutputTooSmall{}:
1701 "TraceStateOutputTooSmall"
1702 case TraceStateInputTooSmall{}:
1703 "TraceStateInputTooSmall"
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716type EntryError is Data:
1717 MissingEquals{}
1718 InvalidKey{}
1719 InvalidValue{}
1720
1721
1722def StateChar.in_range(+code: U32, low: U32, high: U32) -> Bool:
1723 Bool.and(U32.is_ge(code, low), U32.is_le(code, high))
1724
1725
1726
1727def StateChar.is_key_start(char: Char) -> Bool:
1728 match char:
1729 case Chr{+code}:
1730 Bool.or(StateChar.in_range(code, 97, 122), StateChar.in_range(code, 48, 57))
1731
1732
1733def StateChar.is_key(+char: Char) -> Bool:
1734 Bool.or(StateChar.is_key_start(char), Bool.or(Char.is_eq(char, '_'), Bool.or(Char.is_eq(char, '-'),
1735 Bool.or(Char.is_eq(char, '*'), Bool.or(Char.is_eq(char, '/'), Char.is_eq(char, '@'))))))
1736
1737
1738
1739def StateChar.is_value_end(char: Char) -> Bool:
1740 match char:
1741 case Chr{+code}:
1742 Bool.or(StateChar.in_range(code, 33, 43), Bool.or(StateChar.in_range(code, 45, 60),
1743 StateChar.in_range(code, 62, 126)))
1744
1745
1746def StateChar.is_value(+char: Char) -> Bool:
1747 Bool.or(Char.is_eq(char, ' '), StateChar.is_value_end(char))
1748
1749
1750def StateChar.is_ows(+char: Char) -> Bool:
1751 Bool.or(Char.is_eq(char, ' '), Char.is_eq(char, '\t'))
1752
1753
1754
1755
1756def StateKey.valid.rest(text: String, room: Nat) -> Bool:
1757 match text:
1758 case SNil{}:
1759 True{}
1760 case SCon{char, tail}:
1761 match room:
1762 case 0n:
1763 False{}
1764 case 1n+left:
1765 Bool.and(StateChar.is_key(char), StateKey.valid.rest(tail, left))
1766
1767
1768
1769def StateKey.is_valid(text: String) -> Bool:
1770 match text:
1771 case SNil{}:
1772 False{}
1773 case SCon{char, tail}:
1774 Bool.and(StateChar.is_key_start(char), StateKey.valid.rest(tail, 255n))
1775
1776
1777
1778def StateValue.valid.go(tail: String, char: Char, room: Nat) -> Bool:
1779 match tail:
1780 case SNil{}:
1781 StateChar.is_value_end(char)
1782 case SCon{next, rest}:
1783 match room:
1784 case 0n:
1785 False{}
1786 case 1n+left:
1787 Bool.and(StateChar.is_value(char), StateValue.valid.go(rest, next, left))
1788
1789
1790
1791def StateValue.is_valid(text: String) -> Bool:
1792 match text:
1793 case SNil{}:
1794 False{}
1795 case SCon{char, tail}:
1796 StateValue.valid.go(tail, char, 255n)
1797
1798
1799
1800
1801
1802type StateKey is Data:
1803 StateKey{text: String, evidence: {StateKey.is_valid(text) == True{} : Bool}}
1804
1805
1806
1807type StateValue is Data:
1808 StateValue{text: String, evidence: {StateValue.is_valid(text) == True{} : Bool}}
1809
1810
1811def StateKey.parse.checked(text: String, valid: Bool, evidence: {StateKey.is_valid(text) == valid : Bool}) ->
1812 Result<&2, &2, EntryError, StateKey>:
1813 match valid:
1814 case True{}:
1815 Done{StateKey{text, evidence}}
1816 case False{}:
1817 Fail{InvalidKey{}}
1818
1819
1820
1821def StateKey.parse(+text: String) -> Result<&2, &2, EntryError, StateKey>:
1822 StateKey.parse.checked(text, StateKey.is_valid(text), {==})
1823
1824
1825def StateKey.to_string(key: StateKey) -> String:
1826 match key:
1827 case StateKey{text, evidence}:
1828 text
1829
1830
1831def StateValue.parse.checked(text: String, valid: Bool, evidence: {StateValue.is_valid(text) == valid : Bool}) ->
1832 Result<&2, &2, EntryError, StateValue>:
1833 match valid:
1834 case True{}:
1835 Done{StateValue{text, evidence}}
1836 case False{}:
1837 Fail{InvalidValue{}}
1838
1839
1840
1841
1842def StateValue.parse(+text: String) -> Result<&2, &2, EntryError, StateValue>:
1843 StateValue.parse.checked(text, StateValue.is_valid(text), {==})
1844
1845
1846def StateValue.to_string(value: StateValue) -> String:
1847 match value:
1848 case StateValue{text, evidence}:
1849 text
1850
1851
1852def EntryError.show(error: EntryError) -> String:
1853 match error:
1854 case MissingEquals{}:
1855 "MissingEquals"
1856 case InvalidKey{}:
1857 "InvalidKey"
1858 case InvalidValue{}:
1859 "InvalidValue"
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874type StateError is Data:
1875 StateTooLarge{}
1876 TooManyMembers{}
1877 InvalidEntry{member: Nat, error: EntryError}
1878
1879
1880type StateEntry is Data:
1881 StateEntry{key: StateKey, value: StateValue}
1882
1883
1884def StateEntry.key(entry: StateEntry) -> StateKey:
1885 match entry:
1886 case StateEntry{key, value}:
1887 key
1888
1889
1890def StateEntry.value(entry: StateEntry) -> StateValue:
1891 match entry:
1892 case StateEntry{key, value}:
1893 value
1894
1895
1896def StateEntry.key_text(entry: StateEntry) -> String:
1897 StateKey.to_string(StateEntry.key(entry))
1898
1899
1900def StateEntry.format(entry: StateEntry) -> String:
1901 match entry:
1902 case StateEntry{key, value}:
1903 StateKey.to_string(key) ++ "=" ++ StateValue.to_string(value)
1904
1905
1906def Entries.has_key(entries: List<&2, StateEntry>, +key: String) -> Bool:
1907 match entries:
1908 case Nil{}:
1909 False{}
1910 case Con{entry, rest}:
1911 Bool.or(String.eq(StateEntry.key_text(entry), key), Entries.has_key(rest, key))
1912
1913
1914
1915def Entries.unique(entries: List<&2, StateEntry>, +seen: List<&2, StateEntry>) -> Bool:
1916 match entries:
1917 case Nil{}:
1918 True{}
1919 case Con{+entry, rest}:
1920 Bool.and(Bool.not(Entries.has_key(seen, StateEntry.key_text(entry))),
1921 Entries.unique(rest, List.append(&2, StateEntry, seen, Con{entry, Nil{}})))
1922
1923
1924def TraceState.is_valid(+entries: List<&2, StateEntry>) -> Bool:
1925 Bool.and(Entries.unique(entries, Nil{}), Nat.is_le(List.length(&2, StateEntry, entries), 32n))
1926
1927
1928
1929
1930type TraceState is Data:
1931 TraceState{entries: List<&2, StateEntry>, evidence: {TraceState.is_valid(entries) == True{} : Bool}}
1932
1933
1934def TraceState.empty() -> TraceState:
1935 TraceState{Nil{}, {==}}
1936
1937
1938def TraceState.entries(state: TraceState) -> List<&2, StateEntry>:
1939 match state:
1940 case TraceState{entries, evidence}:
1941 entries
1942
1943
1944def TraceState.is_empty(state: TraceState) -> Bool:
1945 List.is_empty(&2, StateEntry, TraceState.entries(state))
1946
1947
1948def Entries.get.put(entry: StateEntry, rest: Maybe<&2, StateValue>, hit: Bool) -> Maybe<&2, StateValue>:
1949 match hit:
1950 case True{}:
1951 Some{StateEntry.value(entry)}
1952 case False{}:
1953 rest
1954
1955
1956def Entries.get(entries: List<&2, StateEntry>, +key: String) -> Maybe<&2, StateValue>:
1957 match entries:
1958 case Nil{}:
1959 None{}
1960 case Con{+entry, rest}:
1961 Entries.get.put(entry, Entries.get(rest, key), String.eq(StateEntry.key_text(entry), key))
1962
1963
1964
1965def TraceState.get(state: TraceState, key: StateKey) -> Maybe<&2, StateValue>:
1966 Entries.get(TraceState.entries(state), StateKey.to_string(key))
1967
1968
1969def Entries.format.rest(entries: List<&2, StateEntry>) -> String:
1970 match entries:
1971 case Nil{}:
1972 ""
1973 case Con{entry, rest}:
1974 "," ++ StateEntry.format(entry) ++ Entries.format.rest(rest)
1975
1976
1977def Entries.format(entries: List<&2, StateEntry>) -> String:
1978 match entries:
1979 case Nil{}:
1980 ""
1981 case Con{entry, rest}:
1982 StateEntry.format(entry) ++ Entries.format.rest(rest)
1983
1984
1985
1986
1987def TraceState.format(state: TraceState) -> String:
1988 Entries.format(TraceState.entries(state))
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999def Utf8.width(char: Char) -> Nat:
2000 match char:
2001 case Chr{+code}:
2002 Bool.pick(Nat, U32.is_lt(code, 128), 1n,
2003 Bool.pick(Nat, U32.is_lt(code, 2048), 2n,
2004 Bool.pick(Nat, U32.is_lt(code, 65536), 3n, 4n)))
2005
2006
2007
2008
2009
2010def Utf8.length(text: String) -> Nat:
2011 match text:
2012 case SNil{}:
2013 0n
2014 case SCon{char, tail}:
2015 Nat.add(Utf8.width(char), Utf8.length(tail))
2016
2017
2018
2019def Budget.take(need: Nat, budget: Nat) -> Maybe<&2, Nat>:
2020 match need budget:
2021 case 0n _:
2022 Some{budget}
2023 case 1n+more 0n:
2024 None{}
2025 case 1n+more 1n+left:
2026 Budget.take(more, left)
2027
2028
2029
2030
2031def Utf8.left(text: String, budget: Maybe<&2, Nat>) -> Maybe<&2, Nat>:
2032 match text budget:
2033 case _ None{}:
2034 None{}
2035 case SNil{} Some{left}:
2036 Some{left}
2037 case SCon{char, tail} Some{left}:
2038 Utf8.left(tail, Budget.take(Utf8.width(char), left))
2039
2040
2041
2042def Utf8.left_more(fields: List<&2, String>, budget: Maybe<&2, Nat>) -> Maybe<&2, Nat>:
2043 match fields budget:
2044 case Nil{} _:
2045 budget
2046 case Con{field, rest} None{}:
2047 None{}
2048 case Con{field, rest} Some{left}:
2049 Utf8.left_more(rest, Utf8.left(field, Utf8.left(",", Some{left})))
2050
2051
2052
2053def Utf8.left_fields(fields: List<&2, String>, budget: Maybe<&2, Nat>) -> Maybe<&2, Nat>:
2054 match fields:
2055 case Nil{}:
2056 budget
2057 case Con{field, rest}:
2058 Utf8.left_more(rest, Utf8.left(field, budget))
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072type Member is Data:
2073 Blank{}
2074 InKey{text: String}
2075 InValue{key: String, text: String}
2076
2077
2078
2079
2080
2081type Scan is Data:
2082 Scanning{member: Nat, room: Nat, current: Member, entries: List<&2, StateEntry>}
2083 Stopped{error: StateError}
2084
2085
2086
2087
2088def Value.restore.step(kept: Bool, ows: Bool, char: Char, value: String) -> Bool & String:
2089 match kept:
2090 case True{}:
2091 (True{}, SCon{char, value})
2092 case False{}:
2093 match ows:
2094 case True{}:
2095 (False{}, value)
2096 case False{}:
2097 (True{}, SCon{char, value})
2098
2099
2100
2101def Value.restore.go(text: String, state: Bool & String) -> String:
2102 match text:
2103 case SNil{}:
2104 (kept, value) = state
2105 value
2106 case SCon{+char, tail}:
2107 (kept, value) = state
2108 Value.restore.go(tail, Value.restore.step(kept, StateChar.is_ows(char), char, value))
2109
2110
2111def Value.restore(text: String) -> String:
2112 Value.restore.go(text, (False{}, ""))
2113
2114
2115
2116
2117def Member.start(ows: Bool, equals: Bool, char: Char) -> Member:
2118 match ows:
2119 case True{}:
2120 Blank{}
2121 case False{}:
2122 match equals:
2123 case True{}:
2124 InValue{"", ""}
2125 case False{}:
2126 InKey{SCon{char, SNil{}}}
2127
2128
2129
2130def Member.key(equals: Bool, char: Char, text: String) -> Member:
2131 match equals:
2132 case True{}:
2133 InValue{String.reverse(text), ""}
2134 case False{}:
2135 InKey{SCon{char, text}}
2136
2137
2138
2139def Member.push(+char: Char, current: Member) -> Member:
2140 match current:
2141 case Blank{}:
2142 Member.start(StateChar.is_ows(char), Char.is_eq(char, '='), char)
2143 case InKey{text}:
2144 Member.key(Char.is_eq(char, '='), char, text)
2145 case InValue{key, text}:
2146 InValue{key, SCon{char, text}}
2147
2148
2149
2150def Scan.validate(key: String, value: String) -> Result<&2, &2, EntryError, StateEntry>:
2151 do Result<&2, &2, EntryError, StateEntry>:
2152 parsed_key : StateKey <- StateKey.parse(key)
2153 parsed_value : StateValue <- StateValue.parse(value)
2154 return StateEntry{parsed_key, parsed_value}
2155
2156
2157
2158def Entries.add.if(entries: List<&2, StateEntry>, entry: StateEntry, present: Bool) -> List<&2, StateEntry>:
2159 match present:
2160 case True{}:
2161 entries
2162 case False{}:
2163 List.append(&2, StateEntry, entries, Con{entry, Nil{}})
2164
2165
2166
2167def Entries.add(+entries: List<&2, StateEntry>, +entry: StateEntry) -> List<&2, StateEntry>:
2168 Entries.add.if(entries, entry, Entries.has_key(entries, StateEntry.key_text(entry)))
2169
2170
2171
2172def Scan.keep(room: Nat, member: Nat, entry: StateEntry, entries: List<&2, StateEntry>) -> Scan:
2173 match room:
2174 case 0n:
2175 Stopped{TooManyMembers{}}
2176 case 1n+left:
2177 Scanning{1n+member, left, Blank{}, Entries.add(entries, entry)}
2178
2179
2180
2181def Scan.entry(parsed: Result<&2, &2, EntryError, StateEntry>, member: Nat, room: Nat,
2182 entries: List<&2, StateEntry>) -> Scan:
2183 match parsed:
2184 case Fail{error}:
2185 Stopped{InvalidEntry{member, error}}
2186 case Done{entry}:
2187 Scan.keep(room, member, entry, entries)
2188
2189
2190
2191
2192
2193def Scan.close(member: Nat, room: Nat, current: Member, entries: List<&2, StateEntry>) -> Scan:
2194 match current:
2195 case Blank{}:
2196 Scanning{1n+member, room, Blank{}, entries}
2197 case InKey{text}:
2198 Stopped{InvalidEntry{member, MissingEquals{}}}
2199 case InValue{key, text}:
2200 Scan.entry(Scan.validate(key, Value.restore(text)), member, room, entries)
2201
2202
2203
2204def Scan.read(comma: Bool, char: Char, member: Nat, room: Nat, current: Member,
2205 entries: List<&2, StateEntry>) -> Scan:
2206 match comma:
2207 case True{}:
2208 Scan.close(member, room, current, entries)
2209 case False{}:
2210 Scanning{member, room, Member.push(char, current), entries}
2211
2212
2213def Scan.char(+char: Char, scan: Scan) -> Scan:
2214 match scan:
2215 case Stopped{error}:
2216 Stopped{error}
2217 case Scanning{member, room, current, entries}:
2218 Scan.read(Char.is_eq(char, ','), char, member, room, current, entries)
2219
2220
2221
2222def Scan.text(text: String, scan: Scan) -> Scan:
2223 match text scan:
2224 case _ Stopped{error}:
2225 Stopped{error}
2226 case SNil{} Scanning{member, room, current, entries}:
2227 Scanning{member, room, current, entries}
2228 case SCon{char, tail} Scanning{member, room, current, entries}:
2229 Scan.text(tail, Scan.char(char, Scanning{member, room, current, entries}))
2230
2231
2232def Scan.more(fields: List<&2, String>, scan: Scan) -> Scan:
2233 match fields:
2234 case Nil{}:
2235 scan
2236 case Con{field, rest}:
2237 Scan.more(rest, Scan.text(field, Scan.char(',', scan)))
2238
2239
2240def Scan.fields(fields: List<&2, String>, scan: Scan) -> Scan:
2241 match fields:
2242 case Nil{}:
2243 scan
2244 case Con{field, rest}:
2245 Scan.more(rest, Scan.text(field, scan))
2246
2247
2248def Scan.start() -> Scan:
2249 Scanning{0n, 32n, Blank{}, Nil{}}
2250
2251
2252
2253
2254
2255def Scan.state(entries: List<&2, StateEntry>, valid: Bool,
2256 evidence: {TraceState.is_valid(entries) == valid : Bool}) -> Result<&2, &2, StateError, TraceState>:
2257 match valid:
2258 case True{}:
2259 Done{TraceState{entries, evidence}}
2260 case False{}:
2261 Fail{TooManyMembers{}}
2262
2263
2264
2265def Scan.result(scan: Scan) -> Result<&2, &2, StateError, TraceState>:
2266 match scan:
2267 case Stopped{error}:
2268 Fail{error}
2269 case Scanning{member, room, current, +entries}:
2270 Scan.state(entries, TraceState.is_valid(entries), {==})
2271
2272
2273def Scan.finish(scan: Scan) -> Result<&2, &2, StateError, TraceState>:
2274 match scan:
2275 case Stopped{error}:
2276 Fail{error}
2277 case Scanning{member, room, current, entries}:
2278 Scan.result(Scan.close(member, room, current, entries))
2279
2280
2281def Scan.within(fits: Maybe<&2, Nat>, fields: List<&2, String>) -> Result<&2, &2, StateError, TraceState>:
2282 match fits:
2283 case None{}:
2284 Fail{StateTooLarge{}}
2285 case Some{left}:
2286 Scan.finish(Scan.fields(fields, Scan.start()))
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300def TraceState.parse_fields(limits: Limits, +fields: List<&2, String>) -> Result<&2, &2, StateError, TraceState>:
2301 Scan.within(Utf8.left_fields(fields, Some{Limits.tracestate_input(limits)}), fields)
2302
2303
2304def TraceState.parse(limits: Limits, text: String) -> Result<&2, &2, StateError, TraceState>:
2305 TraceState.parse_fields(limits, Con{text, Nil{}})
2306
2307
2308
2309def StateError.show(error: StateError) -> String:
2310 match error:
2311 case StateTooLarge{}:
2312 "StateTooLarge"
2313 case TooManyMembers{}:
2314 "TooManyMembers"
2315 case InvalidEntry{member, reason}:
2316 "InvalidEntry " ++ Nat.show(member) ++ " " ++ EntryError.show(reason)
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328def Entries.unless(-A: Data, skip: Bool, item: A, rest: List<&2, A>) -> List<&2, A>:
2329 match skip:
2330 case True{}:
2331 rest
2332 case False{}:
2333 Con{item, rest}
2334
2335
2336def Entries.without(entries: List<&2, StateEntry>, +key: String) -> List<&2, StateEntry>:
2337 match entries:
2338 case Nil{}:
2339 Nil{}
2340 case Con{+entry, rest}:
2341 Entries.unless(StateEntry, String.eq(StateEntry.key_text(entry), key), entry, Entries.without(rest, key))
2342
2343
2344
2345
2346
2347def TraceState.from_entries.checked(entries: List<&2, StateEntry>, fallback: TraceState, valid: Bool,
2348 evidence: {TraceState.is_valid(entries) == valid : Bool}) -> TraceState:
2349 match valid:
2350 case True{}:
2351 TraceState{entries, evidence}
2352 case False{}:
2353 fallback
2354
2355
2356def TraceState.from_entries(+entries: List<&2, StateEntry>, fallback: TraceState) -> TraceState:
2357 TraceState.from_entries.checked(entries, fallback, TraceState.is_valid(entries), {==})
2358
2359
2360
2361
2362
2363
2364def TraceState.set(+state: TraceState, +key: StateKey, value: StateValue) -> TraceState:
2365 TraceState.from_entries(Con{StateEntry{key, value},
2366 List.take(&2, StateEntry, Entries.without(TraceState.entries(state), StateKey.to_string(key)), 31n)}, state)
2367
2368
2369
2370
2371
2372def TraceState.remove(+state: TraceState, key: StateKey) -> TraceState:
2373 TraceState.from_entries(Entries.without(TraceState.entries(state), StateKey.to_string(key)), state)
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384def StateEntry.size(entry: StateEntry) -> Nat:
2385 match entry:
2386 case StateEntry{key, value}:
2387 Nat.add(Utf8.length(StateKey.to_string(key)), 1n+Utf8.length(StateValue.to_string(value)))
2388
2389
2390def Entries.size.rest(entries: List<&2, StateEntry>) -> Nat:
2391 match entries:
2392 case Nil{}:
2393 0n
2394 case Con{entry, rest}:
2395 1n+Nat.add(StateEntry.size(entry), Entries.size.rest(rest))
2396
2397
2398def Entries.size(entries: List<&2, StateEntry>) -> Nat:
2399 match entries:
2400 case Nil{}:
2401 0n
2402 case Con{entry, rest}:
2403 Nat.add(StateEntry.size(entry), Entries.size.rest(rest))
2404
2405
2406def TraceState.size(state: TraceState) -> Nat:
2407 Entries.size(TraceState.entries(state))
2408
2409
2410
2411
2412type Truncation is Data:
2413 Truncation{kept: TraceState, dropped: List<&2, StateKey>}
2414
2415
2416def Truncation.kept(truncation: Truncation) -> TraceState:
2417 match truncation:
2418 case Truncation{kept, dropped}:
2419 kept
2420
2421
2422
2423def Truncation.dropped(truncation: Truncation) -> List<&2, StateKey>:
2424 match truncation:
2425 case Truncation{kept, dropped}:
2426 dropped
2427
2428
2429
2430def StateEntry.is_large(entry: StateEntry) -> Bool:
2431 Nat.is_gt(StateEntry.size(entry), 128n)
2432
2433
2434def Entries.has_large(entries: List<&2, StateEntry>) -> Bool:
2435 match entries:
2436 case Nil{}:
2437 False{}
2438 case Con{entry, rest}:
2439 Bool.or(StateEntry.is_large(entry), Entries.has_large(rest))
2440
2441
2442
2443def Entries.drop_last_large(entries: List<&2, StateEntry>) -> List<&2, StateEntry>:
2444 match entries:
2445 case Nil{}:
2446 Nil{}
2447 case Con{+entry, +rest}:
2448 Bool.pick(List<&2, StateEntry>, Entries.has_large(rest), Con{entry, Entries.drop_last_large(rest)},
2449 Bool.pick(List<&2, StateEntry>, StateEntry.is_large(entry), rest, Con{entry, rest}))
2450
2451
2452def Entries.drop_last(entries: List<&2, StateEntry>) -> List<&2, StateEntry>:
2453 match entries:
2454 case Nil{}:
2455 Nil{}
2456 case Con{entry, +rest}:
2457 Bool.pick(List<&2, StateEntry>, List.is_empty(&2, StateEntry, rest), Nil{},
2458 Con{entry, Entries.drop_last(rest)})
2459
2460
2461
2462def Entries.shrink(+entries: List<&2, StateEntry>) -> List<&2, StateEntry>:
2463 Bool.pick(List<&2, StateEntry>, Entries.has_large(entries), Entries.drop_last_large(entries),
2464 Entries.drop_last(entries))
2465
2466
2467
2468
2469
2470def Entries.truncate(fuel: Nat, +budget: Nat, +entries: List<&2, StateEntry>) -> List<&2, StateEntry>:
2471 match fuel:
2472 case 0n:
2473 entries
2474 case 1n+more:
2475 Bool.pick(List<&2, StateEntry>, Nat.is_le(Entries.size(entries), budget), entries,
2476 Entries.truncate(more, budget, Entries.shrink(entries)))
2477
2478
2479def Entries.dropped(entries: List<&2, StateEntry>, +kept: List<&2, StateEntry>) -> List<&2, StateKey>:
2480 match entries:
2481 case Nil{}:
2482 Nil{}
2483 case Con{+entry, rest}:
2484 Entries.unless(StateKey, Entries.has_key(kept, StateEntry.key_text(entry)), StateEntry.key(entry),
2485 Entries.dropped(rest, kept))
2486
2487
2488
2489
2490
2491
2492
2493def TraceState.truncate(limits: Limits, state: TraceState) -> Truncation:
2494 +entries = TraceState.entries(state)
2495 +kept = Entries.truncate(List.length(&2, StateEntry, entries), Limits.tracestate_output(limits), entries)
2496 Truncation{TraceState.from_entries(kept, TraceState.empty()), Entries.dropped(entries, kept)}
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513type OutgoingContext is Data:
2514 OutgoingContext{context: LocalContext, state: TraceState}
2515
2516
2517
2518
2519
2520type Emission is Data:
2521 Emission{traceparent: String, tracestate: String, dropped: List<&2, StateKey>}
2522
2523
2524def OutgoingContext.new(context: LocalContext) -> OutgoingContext:
2525 OutgoingContext{context, TraceState.empty()}
2526
2527
2528
2529
2530def OutgoingContext.with_state(context: LocalContext, state: TraceState) -> OutgoingContext:
2531 OutgoingContext{context, state}
2532
2533
2534def OutgoingContext.context(outgoing: OutgoingContext) -> LocalContext:
2535 match outgoing:
2536 case OutgoingContext{context, state}:
2537 context
2538
2539
2540def OutgoingContext.state(outgoing: OutgoingContext) -> TraceState:
2541 match outgoing:
2542 case OutgoingContext{context, state}:
2543 state
2544
2545
2546def OutgoingContext.get(outgoing: OutgoingContext, key: StateKey) -> Maybe<&2, StateValue>:
2547 TraceState.get(OutgoingContext.state(outgoing), key)
2548
2549
2550
2551def OutgoingContext.set(outgoing: OutgoingContext, key: StateKey, value: StateValue) -> OutgoingContext:
2552 match outgoing:
2553 case OutgoingContext{context, state}:
2554 OutgoingContext{context, TraceState.set(state, key, value)}
2555
2556
2557
2558def OutgoingContext.remove(outgoing: OutgoingContext, key: StateKey) -> OutgoingContext:
2559 match outgoing:
2560 case OutgoingContext{context, state}:
2561 OutgoingContext{context, TraceState.remove(state, key)}
2562
2563
2564def Emission.of(context: LocalContext, truncation: Truncation) -> Emission:
2565 match truncation:
2566 case Truncation{kept, dropped}:
2567 Emission{TraceParentV00.format(LocalContext.to_traceparent(context)), TraceState.format(kept), dropped}
2568
2569
2570
2571
2572
2573def OutgoingContext.emit(limits: Limits, outgoing: OutgoingContext) -> Emission:
2574 match outgoing:
2575 case OutgoingContext{context, state}:
2576 Emission.of(context, TraceState.truncate(limits, state))
2577
2578
2579def Emission.traceparent(emission: Emission) -> String:
2580 match emission:
2581 case Emission{traceparent, tracestate, dropped}:
2582 traceparent
2583
2584
2585def Emission.tracestate(emission: Emission) -> String:
2586 match emission:
2587 case Emission{traceparent, tracestate, dropped}:
2588 tracestate
2589
2590
2591def Emission.dropped(emission: Emission) -> List<&2, StateKey>:
2592 match emission:
2593 case Emission{traceparent, tracestate, dropped}:
2594 dropped
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617type Header is Data:
2618 Header{name: String, value: String}
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629type TraceParentError is Data:
2630 RepeatedTraceParent{}
2631 TraceParentTooLarge{}
2632 InvalidTraceParent{error: Error}
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642type ReceivedPair is Data:
2643 ReceivedPair{traceparent: String, tracestate: List<&2, String>}
2644
2645
2646
2647
2648
2649
2650
2651
2652type IncomingContext is Data:
2653 IncomingContext{context: RemoteContext, state: TraceState, received: Maybe<&2, ReceivedPair>}
2654
2655
2656
2657
2658type BaseContext is Data:
2659 IncomingBase{incoming: IncomingContext}
2660 OutgoingBase{outgoing: OutgoingContext}
2661
2662
2663
2664type TraceParentOutcome is Data:
2665 TraceParentAccepted{}
2666 TraceParentAbsent{}
2667 TraceParentRejected{error: TraceParentError}
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678type StateOutcome is Data:
2679 StateAbsent{}
2680 StateAccepted{}
2681 StateDiscarded{error: StateError}
2682 StateIgnored{}
2683
2684
2685
2686
2687type Extraction is Data:
2688 Extraction{context: Maybe<&2, BaseContext>, parent: TraceParentOutcome, state: StateOutcome}
2689
2690
2691
2692
2693
2694
2695
2696
2697def Header.name(header: Header) -> String:
2698 match header:
2699 case Header{name, value}:
2700 name
2701
2702
2703def Header.value(header: Header) -> String:
2704 match header:
2705 case Header{name, value}:
2706 value
2707
2708
2709
2710
2711
2712def Carrier.traceparent_name() -> String:
2713 "traceparent"
2714
2715def Carrier.tracestate_name() -> String:
2716 "tracestate"
2717
2718
2719
2720
2721
2722
2723
2724def Carrier.named(name: String, text: String) -> Bool:
2725 match name text:
2726 case SNil{} SNil{}:
2727 True{}
2728 case SNil{} SCon{char, rest}:
2729 False{}
2730 case SCon{expected, more} SNil{}:
2731 False{}
2732 case SCon{expected, more} SCon{char, rest}:
2733 Bool.and(Char.is_eq(Char.to_lower(char), expected), Carrier.named(more, rest))
2734
2735
2736def Carrier.keep(hit: Bool, value: String, found: List<&2, String>) -> List<&2, String>:
2737 match hit:
2738 case True{}:
2739 Con{value, found}
2740 case False{}:
2741 found
2742
2743
2744def Carrier.values.go(carrier: List<&2, Header>, +name: String, found: List<&2, String>) -> List<&2, String>:
2745 match carrier:
2746 case Nil{}:
2747 found
2748 case Con{+header, rest}:
2749 Carrier.values.go(rest, name, Carrier.keep(Carrier.named(name, Header.name(header)), Header.value(header), found))
2750
2751
2752
2753
2754def Carrier.values(carrier: List<&2, Header>, +name: String) -> List<&2, String>:
2755 List.reverse(&2, String, Carrier.values.go(carrier, name, Nil{}))
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768def Text.drop_ows.go(tail: String, head: Char, ows: Bool) -> String:
2769 match tail ows:
2770 case SNil{} False{}:
2771 SCon{head, SNil{}}
2772 case SCon{next, rest} False{}:
2773 SCon{head, SCon{next, rest}}
2774 case SNil{} True{}:
2775 SNil{}
2776 case SCon{+next, rest} True{}:
2777 Text.drop_ows.go(rest, next, StateChar.is_ows(next))
2778
2779
2780
2781def Text.drop_ows(text: String) -> String:
2782 match text:
2783 case SNil{}:
2784 SNil{}
2785 case SCon{+head, tail}:
2786 Text.drop_ows.go(tail, head, StateChar.is_ows(head))
2787
2788
2789
2790
2791
2792def Text.trim_ows(text: String) -> String:
2793 String.reverse(Text.drop_ows(String.reverse(Text.drop_ows(text))))
2794
2795
2796
2797def Text.has_comma.go(text: String, found: Bool) -> Bool:
2798 match text found:
2799 case _ True{}:
2800 True{}
2801 case SNil{} False{}:
2802 False{}
2803 case SCon{char, rest} False{}:
2804 Text.has_comma.go(rest, Char.is_eq(char, ','))
2805
2806
2807def Text.has_comma(text: String) -> Bool:
2808 Text.has_comma.go(text, False{})
2809
2810
2811
2812
2813
2814def Text.is_control(char: Char) -> Bool:
2815 match char:
2816 case Chr{+code}:
2817 Bool.or(Bool.and(U32.is_lt(code, 32), Bool.not(U32.is_eq(code, 9))), U32.is_eq(code, 127))
2818
2819
2820
2821def Text.control.go(text: String, +offset: Nat, found: Maybe<&2, Nat>) -> Maybe<&2, Nat>:
2822 match text found:
2823 case _ Some{at}:
2824 Some{at}
2825 case SNil{} None{}:
2826 None{}
2827 case SCon{char, rest} None{}:
2828 Text.control.go(rest, 1n+offset, Bool.pick(Maybe<&2, Nat>, Text.is_control(char), Some{offset}, None{}))
2829
2830
2831
2832
2833
2834def Read.is_later(digits: Digits.Digits(2n)) -> Result<&2, &2, Error, Bool>:
2835 match digits:
2836 case Digits.DCon{Hex.H0{}, Digits.DCon{Hex.H0{}, Digits.DNil{}}}:
2837 Done{False{}}
2838 case Digits.DCon{Hex.Hf{}, Digits.DCon{Hex.Hf{}, Digits.DNil{}}}:
2839 Fail{ForbiddenVersion{}}
2840 case other:
2841 Done{True{}}
2842
2843
2844def Read.is_later.parsed(parsed: Parsed<2n>) -> Result<&2, &2, Error, Bool>:
2845 match parsed:
2846 case Parsed{digits, rest}:
2847 Read.is_later(digits)
2848
2849
2850
2851def Read.clean(found: Maybe<&2, Nat>) -> Result<&2, &2, Error, Unit>:
2852 match found:
2853 case None{}:
2854 Done{Unit{}}
2855 case Some{offset}:
2856 Fail{ControlCharacter{offset}}
2857
2858
2859
2860
2861
2862
2863
2864
2865def Read.extension(rest: String) -> Result<&2, &2, Error, Unit>:
2866 match rest:
2867 case SNil{}:
2868 Done{Unit{}}
2869 case SCon{char, tail}:
2870 do Result<&2, &2, Error, Unit>:
2871 unknown : String <- Parse.separator_result(tail, Char.is_eq(char, '-'), 55n)
2872 Read.clean(Text.control.go(unknown, 56n, None{}))
2873
2874
2875
2876
2877
2878
2879def Read.prefix(+text: String) -> Result<&2, &2, Error, TraceParentV00>:
2880 do Result<&2, &2, Error, TraceParentV00>:
2881 known : TraceParentV00 <- TraceParentV00.parse("00" ++ String.drop(String.take(text, 55n), 2n))
2882 rest : Unit <- Read.extension(String.drop(text, 55n))
2883 return known
2884
2885
2886def Read.by_version(later: Bool, text: String) -> Result<&2, &2, Error, TraceParentV00>:
2887 match later:
2888 case False{}:
2889 TraceParentV00.parse(text)
2890 case True{}:
2891 Read.prefix(text)
2892
2893
2894def Read.known(+text: String) -> Result<&2, &2, Error, TraceParentV00>:
2895 do Result<&2, &2, Error, TraceParentV00>:
2896 version : Parsed<2n> <- Parse.digits(2n, text, 0n)
2897 later : Bool <- Read.is_later.parsed(version)
2898 Read.by_version(later, text)
2899
2900
2901def Read.invalid(result: Result<&2, &2, Error, TraceParentV00>) ->
2902 Result<&2, &2, TraceParentError, TraceParentV00>:
2903 match result:
2904 case Fail{error}:
2905 Fail{InvalidTraceParent{error}}
2906 case Done{value}:
2907 Done{value}
2908
2909
2910
2911def Read.single(combined: Bool, text: String) -> Result<&2, &2, TraceParentError, TraceParentV00>:
2912 match combined:
2913 case True{}:
2914 Fail{RepeatedTraceParent{}}
2915 case False{}:
2916 Read.invalid(Read.known(text))
2917
2918
2919def Read.trimmed(+text: String) -> Result<&2, &2, TraceParentError, TraceParentV00>:
2920 Read.single(Text.has_comma(text), text)
2921
2922
2923
2924def Read.within(fits: Maybe<&2, Nat>, value: String) -> Result<&2, &2, TraceParentError, TraceParentV00>:
2925 match fits:
2926 case None{}:
2927 Fail{TraceParentTooLarge{}}
2928 case Some{left}:
2929 Read.trimmed(Text.trim_ows(value))
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951def TraceParent.read(limits: Limits, +value: String) -> Result<&2, &2, TraceParentError, TraceParentV00>:
2952 Read.within(Utf8.left(value, Some{Limits.traceparent_input(limits)}), value)
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963def Extract.ignored(states: List<&2, String>) -> StateOutcome:
2964 match states:
2965 case Nil{}:
2966 StateAbsent{}
2967 case Con{field, rest}:
2968 StateIgnored{}
2969
2970
2971
2972def Extract.kept(base: Maybe<&2, BaseContext>, parent: TraceParentOutcome, states: List<&2, String>) -> Extraction:
2973 Extraction{base, parent, Extract.ignored(states)}
2974
2975
2976
2977def Extract.incoming(remote: RemoteContext, state: TraceState, received: Maybe<&2, ReceivedPair>) ->
2978 Maybe<&2, BaseContext>:
2979 Some{IncomingBase{IncomingContext{remote, state, received}}}
2980
2981
2982
2983
2984
2985def Extract.parsed(parsed: Result<&2, &2, StateError, TraceState>, remote: RemoteContext, text: String,
2986 states: List<&2, String>) -> Extraction:
2987 match parsed:
2988 case Fail{error}:
2989 Extraction{Extract.incoming(remote, TraceState.empty(), None{}), TraceParentAccepted{}, StateDiscarded{error}}
2990 case Done{state}:
2991 Extraction{Extract.incoming(remote, state, Some{ReceivedPair{text, states}}), TraceParentAccepted{},
2992 StateAccepted{}}
2993
2994
2995
2996def Extract.state(+limits: Limits, +states: List<&2, String>, remote: RemoteContext, text: String) -> Extraction:
2997 match states:
2998 case Nil{}:
2999 Extraction{Extract.incoming(remote, TraceState.empty(), Some{ReceivedPair{text, Nil{}}}), TraceParentAccepted{},
3000 StateAbsent{}}
3001 case Con{field, rest}:
3002 Extract.parsed(TraceState.parse_fields(limits, Con{field, rest}), remote, text, Con{field, rest})
3003
3004
3005
3006
3007def Extract.read(read: Result<&2, &2, TraceParentError, TraceParentV00>, value: String, +limits: Limits,
3008 +states: List<&2, String>, base: Maybe<&2, BaseContext>) -> Extraction:
3009 match read:
3010 case Fail{error}:
3011 Extract.kept(base, TraceParentRejected{error}, states)
3012 case Done{known}:
3013 Extract.state(limits, states, RemoteContext.from_traceparent(known), Text.trim_ows(value))
3014
3015
3016
3017def Extract.parents(+limits: Limits, parents: List<&2, String>, +states: List<&2, String>,
3018 base: Maybe<&2, BaseContext>) -> Extraction:
3019 match parents:
3020 case Nil{}:
3021 Extract.kept(base, TraceParentAbsent{}, states)
3022 case Con{+value, Nil{}}:
3023 Extract.read(TraceParent.read(limits, value), value, limits, states, base)
3024 case Con{first, Con{second, more}}:
3025 Extract.kept(base, TraceParentRejected{RepeatedTraceParent{}}, states)
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042def Context.extract(+limits: Limits, +carrier: List<&2, Header>, base: Maybe<&2, BaseContext>) -> Extraction:
3043 Extract.parents(limits, Carrier.values(carrier, Carrier.traceparent_name()),
3044 Carrier.values(carrier, Carrier.tracestate_name()), base)
3045
3046
3047
3048
3049
3050
3051def Extraction.context(extraction: Extraction) -> Maybe<&2, BaseContext>:
3052 match extraction:
3053 case Extraction{context, parent, state}:
3054 context
3055
3056
3057def Extraction.parent(extraction: Extraction) -> TraceParentOutcome:
3058 match extraction:
3059 case Extraction{context, parent, state}:
3060 parent
3061
3062
3063def Extraction.state(extraction: Extraction) -> StateOutcome:
3064 match extraction:
3065 case Extraction{context, parent, state}:
3066 state
3067
3068
3069def Extraction.incoming.base(context: Maybe<&2, BaseContext>) -> Maybe<&2, IncomingContext>:
3070 match context:
3071 case Some{IncomingBase{incoming}}:
3072 Some{incoming}
3073 case _:
3074 None{}
3075
3076
3077
3078def Extraction.incoming.of(parent: TraceParentOutcome, context: Maybe<&2, BaseContext>) ->
3079 Maybe<&2, IncomingContext>:
3080 match parent:
3081 case TraceParentAccepted{}:
3082 Extraction.incoming.base(context)
3083 case TraceParentAbsent{}:
3084 None{}
3085 case TraceParentRejected{error}:
3086 None{}
3087
3088
3089
3090def Extraction.incoming(extraction: Extraction) -> Maybe<&2, IncomingContext>:
3091 match extraction:
3092 case Extraction{context, parent, state}:
3093 Extraction.incoming.of(parent, context)
3094
3095
3096def IncomingContext.context(incoming: IncomingContext) -> RemoteContext:
3097 match incoming:
3098 case IncomingContext{context, state, received}:
3099 context
3100
3101
3102
3103def IncomingContext.state(incoming: IncomingContext) -> TraceState:
3104 match incoming:
3105 case IncomingContext{context, state, received}:
3106 state
3107
3108
3109
3110def IncomingContext.received(incoming: IncomingContext) -> Maybe<&2, ReceivedPair>:
3111 match incoming:
3112 case IncomingContext{context, state, received}:
3113 received
3114
3115
3116def IncomingContext.parent(incoming: IncomingContext) -> Parent:
3117 RemoteParent{IncomingContext.context(incoming)}
3118
3119
3120
3121
3122
3123
3124
3125def IncomingContext.from_remote(context: RemoteContext, state: TraceState) -> IncomingContext:
3126 IncomingContext{context, state, None{}}
3127
3128
3129def ReceivedPair.traceparent(pair: ReceivedPair) -> String:
3130 match pair:
3131 case ReceivedPair{traceparent, tracestate}:
3132 traceparent
3133
3134
3135def ReceivedPair.tracestate(pair: ReceivedPair) -> List<&2, String>:
3136 match pair:
3137 case ReceivedPair{traceparent, tracestate}:
3138 tracestate
3139
3140
3141
3142def BaseContext.parent(base: BaseContext) -> Parent:
3143 match base:
3144 case IncomingBase{incoming}:
3145 IncomingContext.parent(incoming)
3146 case OutgoingBase{outgoing}:
3147 LocalParent{OutgoingContext.context(outgoing)}
3148
3149
3150
3151def BaseContext.state(base: BaseContext) -> TraceState:
3152 match base:
3153 case IncomingBase{incoming}:
3154 IncomingContext.state(incoming)
3155 case OutgoingBase{outgoing}:
3156 OutgoingContext.state(outgoing)
3157
3158
3159def TraceParentError.show(error: TraceParentError) -> String:
3160 match error:
3161 case RepeatedTraceParent{}:
3162 "RepeatedTraceParent"
3163 case TraceParentTooLarge{}:
3164 "TraceParentTooLarge"
3165 case InvalidTraceParent{reason}:
3166 "InvalidTraceParent " ++ Error.show(reason)
3167
3168
3169def TraceParentOutcome.show(outcome: TraceParentOutcome) -> String:
3170 match outcome:
3171 case TraceParentAccepted{}:
3172 "TraceParentAccepted"
3173 case TraceParentAbsent{}:
3174 "TraceParentAbsent"
3175 case TraceParentRejected{error}:
3176 "TraceParentRejected " ++ TraceParentError.show(error)
3177
3178
3179def StateOutcome.show(outcome: StateOutcome) -> String:
3180 match outcome:
3181 case StateAbsent{}:
3182 "StateAbsent"
3183 case StateAccepted{}:
3184 "StateAccepted"
3185 case StateDiscarded{error}:
3186 "StateDiscarded " ++ StateError.show(error)
3187 case StateIgnored{}:
3188 "StateIgnored"
3189
3190
3191
3192
3193def Extraction.show(extraction: Extraction) -> String:
3194 match extraction:
3195 case Extraction{context, parent, state}:
3196 TraceParentOutcome.show(parent) ++ ", " ++ StateOutcome.show(state)
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212type Injection is Data:
3213 Injection{carrier: List<&2, Header>, dropped: List<&2, StateKey>}
3214
3215
3216
3217def Carrier.is_context(+name: String) -> Bool:
3218 Bool.or(Carrier.named(Carrier.traceparent_name(), name), Carrier.named(Carrier.tracestate_name(), name))
3219
3220
3221
3222def Carrier.keep_unrelated(context_field: Bool, header: Header, kept: List<&2, Header>) -> List<&2, Header>:
3223 match context_field:
3224 case True{}:
3225 kept
3226 case False{}:
3227 Con{header, kept}
3228
3229
3230
3231
3232def Carrier.unrelated.go(carrier: List<&2, Header>, kept: List<&2, Header>) -> List<&2, Header>:
3233 match carrier:
3234 case Nil{}:
3235 kept
3236 case Con{+header, rest}:
3237 Carrier.unrelated.go(rest, Carrier.keep_unrelated(Carrier.is_context(Header.name(header)), header, kept))
3238
3239
3240
3241def Carrier.replace(carrier: List<&2, Header>, fields: List<&2, Header>) -> List<&2, Header>:
3242 List.reverse.go(&2, Header, Carrier.unrelated.go(carrier, Nil{}), fields)
3243
3244
3245
3246
3247
3248
3249
3250def Context.field_names() -> List<&2, String>:
3251 [Carrier.traceparent_name(), Carrier.tracestate_name()]
3252
3253
3254
3255def Carrier.context_fields(traceparent: String, +tracestate: String) -> List<&2, Header>:
3256 Con{Header{Carrier.traceparent_name(), traceparent}, Bool.pick(List<&2, Header>, String.is_empty(tracestate),
3257 Nil{}, Con{Header{Carrier.tracestate_name(), tracestate}, Nil{}})}
3258
3259
3260
3261
3262
3263def Context.clear(carrier: List<&2, Header>) -> List<&2, Header>:
3264 Carrier.replace(carrier, Nil{})
3265
3266
3267def Injection.of(carrier: List<&2, Header>, emission: Emission) -> Injection:
3268 match emission:
3269 case Emission{traceparent, tracestate, dropped}:
3270 Injection{Carrier.replace(carrier, Carrier.context_fields(traceparent, tracestate)), dropped}
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281def Context.inject(limits: Limits, outgoing: OutgoingContext, carrier: List<&2, Header>) -> Injection:
3282 Injection.of(carrier, OutgoingContext.emit(limits, outgoing))
3283
3284
3285def Injection.carrier(injection: Injection) -> List<&2, Header>:
3286 match injection:
3287 case Injection{carrier, dropped}:
3288 carrier
3289
3290
3291
3292def Injection.dropped(injection: Injection) -> List<&2, StateKey>:
3293 match injection:
3294 case Injection{carrier, dropped}:
3295 dropped
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324type ForwardError is Data:
3325 NothingToForward{}
3326 ForwardTooLarge{}
3327 InvalidForwardParent{error: TraceParentError}
3328 InvalidForwardState{error: StateError}
3329
3330
3331
3332def Text.joined.go(fields: List<&2, String>, done: String) -> String:
3333 match fields:
3334 case Nil{}:
3335 done
3336 case Con{field, rest}:
3337 Text.joined.go(rest, String.reverse.go(field, SCon{',', done}))
3338
3339
3340
3341
3342def Text.joined(fields: List<&2, String>) -> String:
3343 match fields:
3344 case Nil{}:
3345 SNil{}
3346 case Con{field, rest}:
3347 String.reverse(Text.joined.go(rest, String.reverse.go(field, SNil{})))
3348
3349
3350
3351
3352def Forward.carrier(carrier: List<&2, Header>, traceparent: String, tracestate: List<&2, String>) -> List<&2, Header>:
3353 Carrier.replace(carrier, Carrier.context_fields(traceparent, Text.joined(tracestate)))
3354
3355
3356def Forward.state(parsed: Result<&2, &2, StateError, TraceState>, carrier: List<&2, Header>, traceparent: String,
3357 tracestate: List<&2, String>) -> Result<&2, &2, ForwardError, List<&2, Header>>:
3358 match parsed:
3359 case Fail{error}:
3360 Fail{InvalidForwardState{error}}
3361 case Done{state}:
3362 Done{Forward.carrier(carrier, traceparent, tracestate)}
3363
3364
3365def Forward.parent(read: Result<&2, &2, TraceParentError, TraceParentV00>, +limits: Limits, carrier: List<&2, Header>,
3366 traceparent: String, +tracestate: List<&2, String>) -> Result<&2, &2, ForwardError, List<&2, Header>>:
3367 match read:
3368 case Fail{error}:
3369 Fail{InvalidForwardParent{error}}
3370 case Done{known}:
3371 Forward.state(TraceState.parse_fields(limits, tracestate), carrier, traceparent, tracestate)
3372
3373
3374
3375def Forward.fits(fits: Maybe<&2, Nat>, +limits: Limits, carrier: List<&2, Header>, +traceparent: String,
3376 +tracestate: List<&2, String>) -> Result<&2, &2, ForwardError, List<&2, Header>>:
3377 match fits:
3378 case None{}:
3379 Fail{ForwardTooLarge{}}
3380 case Some{left}:
3381 Forward.parent(TraceParent.read(limits, traceparent), limits, carrier, traceparent, tracestate)
3382
3383
3384def Forward.pair(+limits: Limits, received: Maybe<&2, ReceivedPair>, carrier: List<&2, Header>) ->
3385 Result<&2, &2, ForwardError, List<&2, Header>>:
3386 match received:
3387 case None{}:
3388 Fail{NothingToForward{}}
3389 case Some{ReceivedPair{+traceparent, +tracestate}}:
3390 Forward.fits(Utf8.left_fields(tracestate, Some{Limits.tracestate_output(limits)}), limits, carrier, traceparent,
3391 tracestate)
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406def Context.forward(+limits: Limits, incoming: IncomingContext, carrier: List<&2, Header>) ->
3407 Result<&2, &2, ForwardError, List<&2, Header>>:
3408 Forward.pair(limits, IncomingContext.received(incoming), carrier)
3409
3410
3411def ForwardError.show(error: ForwardError) -> String:
3412 match error:
3413 case NothingToForward{}:
3414 "NothingToForward"
3415 case ForwardTooLarge{}:
3416 "ForwardTooLarge"
3417 case InvalidForwardParent{reason}:
3418 "InvalidForwardParent " ++ TraceParentError.show(reason)
3419 case InvalidForwardState{reason}:
3420 "InvalidForwardState " ++ StateError.show(reason)
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443type Reception is Data:
3444 Continue{}
3445 Restart{}
3446
3447
3448
3449
3450
3451type FailurePolicy is Data:
3452 Lenient{}
3453 Strict{}
3454
3455
3456
3457
3458
3459type Origin is Data:
3460 Continued{}
3461 Started{}
3462 Restarted{}
3463
3464
3465
3466
3467
3468
3469type Service is Data:
3470 Operating{origin: Origin, outgoing: OutgoingContext, received: Maybe<&2, IncomingContext>}
3471 Untraced{error: GenerationError, received: Maybe<&2, IncomingContext>}
3472
3473
3474
3475def FailurePolicy.result(policy: FailurePolicy, A: Data) -> Data:
3476 match policy:
3477 case Lenient{}:
3478 A
3479 case Strict{}:
3480 Result<&2, &2, GenerationError, A>
3481
3482
3483
3484def Policy.apply(policy: FailurePolicy, -A: Data, strict: A -> Result<&2, &2, GenerationError, A>, value: A) ->
3485 FailurePolicy.result(policy, A):
3486 match policy:
3487 case Lenient{}:
3488 value
3489 case Strict{}:
3490 strict(value)
3491
3492
3493def Policy.apply.done(-S: Type, policy: FailurePolicy, -A: Data, strict: A -> Result<&2, &2, GenerationError, A>,
3494 done: S & A) -> S & FailurePolicy.result(policy, A):
3495 (source, value) = done
3496 (source, Policy.apply(policy, A, strict, value))
3497
3498
3499
3500
3501
3502
3503
3504
3505def Serve.new_trace_sampled(sampling: Sampling) -> Bool:
3506 Sampling.resolve(sampling, False{})
3507
3508
3509
3510def Serve.from_child(state: TraceState, received: Maybe<&2, IncomingContext>,
3511 result: Result<&2, &2, GenerationError, LocalContext>) -> Service:
3512 match result:
3513 case Fail{error}:
3514 Untraced{error, received}
3515 case Done{context}:
3516 Operating{Continued{}, OutgoingContext.with_state(context, state), received}
3517
3518
3519
3520
3521def Serve.from_start(origin: Origin, result: Result<&2, &2, GenerationError, LocalContext>) -> Service:
3522 match result:
3523 case Fail{error}:
3524 Untraced{error, None{}}
3525 case Done{context}:
3526 Operating{origin, OutgoingContext.new(context), None{}}
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540type ServicePlan is Data:
3541 ContinuePlan{generation: Generation, state: TraceState, received: Maybe<&2, IncomingContext>}
3542 StartPlan{generation: Generation, origin: Origin}
3543
3544
3545
3546
3547def Serve.replaced(base: BaseContext) -> RemoteContext:
3548 match base:
3549 case IncomingBase{incoming}:
3550 IncomingContext.context(incoming)
3551 case OutgoingBase{outgoing}:
3552 RemoteContext.from_traceparent(LocalContext.to_traceparent(OutgoingContext.context(outgoing)))
3553
3554
3555
3556def Serve.usable(+base: BaseContext, received: Maybe<&2, IncomingContext>, reception: Reception,
3557 sampling: Sampling) -> ServicePlan:
3558 match reception:
3559 case Continue{}:
3560 ContinuePlan{Generation.child(BaseContext.parent(base), sampling), BaseContext.state(base), received}
3561 case Restart{}:
3562 StartPlan{Generation.restart(Serve.replaced(base), Serve.new_trace_sampled(sampling)), Restarted{}}
3563
3564
3565def Serve.kept(context: Maybe<&2, BaseContext>, received: Maybe<&2, IncomingContext>, reception: Reception,
3566 sampling: Sampling) -> ServicePlan:
3567 match context:
3568 case None{}:
3569 StartPlan{Generation.root(Serve.new_trace_sampled(sampling)), Started{}}
3570 case Some{+base}:
3571 Serve.usable(base, received, reception, sampling)
3572
3573
3574def ServicePlan.new(+extraction: Extraction, reception: Reception, sampling: Sampling) -> ServicePlan:
3575 Serve.kept(Extraction.context(extraction), Extraction.incoming(extraction), reception, sampling)
3576
3577
3578def ServicePlan.generation(plan: ServicePlan) -> Generation:
3579 match plan:
3580 case ContinuePlan{generation, state, received}:
3581 generation
3582 case StartPlan{generation, origin}:
3583 generation
3584
3585
3586
3587def ServicePlan.service(plan: ServicePlan, result: Result<&2, &2, GenerationError, LocalContext>) -> Service:
3588 match plan:
3589 case ContinuePlan{generation, state, received}:
3590 Serve.from_child(state, received, result)
3591 case StartPlan{generation, origin}:
3592 Serve.from_start(origin, result)
3593
3594
3595def Serve.finish(-S: Type, plan: ServicePlan, done: S & Result<&2, &2, GenerationError, LocalContext>) ->
3596 S & Service:
3597 (source, result) = done
3598 (source, ServicePlan.service(plan, result))
3599
3600
3601def Serve.run(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), source: S, +plan: ServicePlan) ->
3602 IO(S & Service):
3603 IO.bind(S & Result<&2, &2, GenerationError, LocalContext>, S & Service,
3604 Generation.run_with(~S, ~read, source, ServicePlan.generation(plan)),
3605 done => IO.pure(S & Service, Serve.finish(S, plan, done)))
3606
3607
3608def Serve.operation(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), source: S,
3609 +extraction: Extraction, reception: Reception, sampling: Sampling) -> IO(S & Service):
3610 Serve.run(~S, ~read, source, ServicePlan.new(extraction, reception, sampling))
3611
3612
3613def Serve.strict(service: Service) -> Result<&2, &2, GenerationError, Service>:
3614 match service:
3615 case Operating{origin, outgoing, received}:
3616 Done{Operating{origin, outgoing, received}}
3617 case Untraced{error, received}:
3618 Fail{error}
3619
3620
3621
3622
3623def Context.continue_or_start_with(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), source: S,
3624 +extraction: Extraction, reception: Reception, sampling: Sampling, +policy: FailurePolicy) ->
3625 IO(S & FailurePolicy.result(policy, Service)):
3626 IO.bind(S & Service, S & FailurePolicy.result(policy, Service),
3627 Serve.operation(~S, ~read, source, extraction, reception, sampling),
3628 done => IO.pure(S & FailurePolicy.result(policy, Service), Policy.apply.done(S, policy, Service, Serve.strict,
3629 done)))
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651type Unforwarded is Data:
3652 NothingKept{}
3653 ForwardFailed{error: ForwardError}
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663type Sent is Data:
3664 Fresh{operation: LocalContext, injection: Injection}
3665 Forwarded{carrier: List<&2, Header>, error: GenerationError}
3666 NoContext{carrier: List<&2, Header>, error: GenerationError, reason: Unforwarded}
3667
3668
3669def Send.forwarded(result: Result<&2, &2, ForwardError, List<&2, Header>>, error: GenerationError,
3670 carrier: List<&2, Header>) -> Sent:
3671 match result:
3672 case Done{forwarded}:
3673 Forwarded{forwarded, error}
3674 case Fail{reason}:
3675 NoContext{Context.clear(carrier), error, ForwardFailed{reason}}
3676
3677
3678
3679
3680def Send.fallback(+limits: Limits, received: Maybe<&2, IncomingContext>, error: GenerationError,
3681 +carrier: List<&2, Header>) -> Sent:
3682 match received:
3683 case None{}:
3684 NoContext{Context.clear(carrier), error, NothingKept{}}
3685 case Some{incoming}:
3686 Send.forwarded(Context.forward(limits, incoming, carrier), error, carrier)
3687
3688
3689
3690def Send.of(+limits: Limits, +outgoing: OutgoingContext, received: Maybe<&2, IncomingContext>,
3691 +carrier: List<&2, Header>, result: Result<&2, &2, GenerationError, LocalContext>) -> Sent:
3692 match result:
3693 case Fail{error}:
3694 Send.fallback(limits, received, error, carrier)
3695 case Done{+operation}:
3696 Fresh{operation, Context.inject(limits, OutgoingContext.with_state(operation, OutgoingContext.state(outgoing)),
3697 carrier)}
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708type SendPlan is Data:
3709 SendChild{generation: Generation, limits: Limits, outgoing: OutgoingContext,
3710 received: Maybe<&2, IncomingContext>, carrier: List<&2, Header>}
3711 SendFallback{error: GenerationError, limits: Limits, received: Maybe<&2, IncomingContext>,
3712 carrier: List<&2, Header>}
3713
3714
3715def SendPlan.new(limits: Limits, service: Service, sampling: Sampling, carrier: List<&2, Header>) -> SendPlan:
3716 match service:
3717 case Operating{origin, +outgoing, received}:
3718 SendChild{Generation.child(LocalParent{OutgoingContext.context(outgoing)}, sampling), limits, outgoing, received,
3719 carrier}
3720 case Untraced{error, received}:
3721 SendFallback{error, limits, received, carrier}
3722
3723
3724
3725
3726def SendPlan.generation(plan: SendPlan) -> Generation:
3727 match plan:
3728 case SendChild{generation, limits, outgoing, received, carrier}:
3729 generation
3730 case SendFallback{error, limits, received, carrier}:
3731 Generation{0n, Failed{error}}
3732
3733
3734
3735
3736def SendPlan.sent(plan: SendPlan, result: Result<&2, &2, GenerationError, LocalContext>) -> Sent:
3737 match plan:
3738 case SendChild{generation, limits, outgoing, received, carrier}:
3739 Send.of(limits, outgoing, received, carrier, result)
3740 case SendFallback{error, limits, received, carrier}:
3741 Send.fallback(limits, received, error, carrier)
3742
3743
3744def Send.finish(-S: Type, plan: SendPlan, done: S & Result<&2, &2, GenerationError, LocalContext>) -> S & Sent:
3745 (source, result) = done
3746 (source, SendPlan.sent(plan, result))
3747
3748
3749def Send.run(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), source: S, +plan: SendPlan) ->
3750 IO(S & Sent):
3751 IO.bind(S & Result<&2, &2, GenerationError, LocalContext>, S & Sent,
3752 Generation.run_with(~S, ~read, source, SendPlan.generation(plan)),
3753 done => IO.pure(S & Sent, Send.finish(S, plan, done)))
3754
3755
3756def Send.strict(sent: Sent) -> Result<&2, &2, GenerationError, Sent>:
3757 match sent:
3758 case Fresh{operation, injection}:
3759 Done{Fresh{operation, injection}}
3760 case Forwarded{carrier, error}:
3761 Fail{error}
3762 case NoContext{carrier, error, reason}:
3763 Fail{error}
3764
3765
3766
3767
3768
3769def Context.send_with(~S: Type, ~read: S -> IO(S & Result<&1, &1, U32 & String, U32>), source: S, +limits: Limits,
3770 service: Service, sampling: Sampling, +policy: FailurePolicy, +carrier: List<&2, Header>) ->
3771 IO(S & FailurePolicy.result(policy, Sent)):
3772 IO.bind(S & Sent, S & FailurePolicy.result(policy, Sent),
3773 Send.run(~S, ~read, source, SendPlan.new(limits, service, sampling, carrier)),
3774 done => IO.pure(S & FailurePolicy.result(policy, Sent), Policy.apply.done(S, policy, Sent, Send.strict, done)))
3775
3776
3777def Origin.show(origin: Origin) -> String:
3778 match origin:
3779 case Continued{}:
3780 "Continued"
3781 case Started{}:
3782 "Started"
3783 case Restarted{}:
3784 "Restarted"
3785
3786
3787def Service.origin(service: Service) -> Maybe<&2, Origin>:
3788 match service:
3789 case Operating{origin, outgoing, received}:
3790 Some{origin}
3791 case Untraced{error, received}:
3792 None{}
3793
3794
3795def Service.outgoing(service: Service) -> Maybe<&2, OutgoingContext>:
3796 match service:
3797 case Operating{origin, outgoing, received}:
3798 Some{outgoing}
3799 case Untraced{error, received}:
3800 None{}
3801
3802
3803def Service.error(service: Service) -> Maybe<&2, GenerationError>:
3804 match service:
3805 case Operating{origin, outgoing, received}:
3806 None{}
3807 case Untraced{error, received}:
3808 Some{error}
3809
3810
3811
3812
3813def Service.set(service: Service, key: StateKey, value: StateValue) -> Service:
3814 match service:
3815 case Operating{origin, outgoing, received}:
3816 Operating{origin, OutgoingContext.set(outgoing, key, value), received}
3817 case Untraced{error, received}:
3818 Untraced{error, received}
3819
3820
3821
3822def Service.remove(service: Service, key: StateKey) -> Service:
3823 match service:
3824 case Operating{origin, outgoing, received}:
3825 Operating{origin, OutgoingContext.remove(outgoing, key), received}
3826 case Untraced{error, received}:
3827 Untraced{error, received}
3828
3829
3830
3831def Service.show(service: Service) -> String:
3832 match service:
3833 case Operating{origin, outgoing, received}:
3834 Origin.show(origin)
3835 case Untraced{error, received}:
3836 "Untraced " ++ GenerationError.show(error)
3837
3838
3839def Unforwarded.show(reason: Unforwarded) -> String:
3840 match reason:
3841 case NothingKept{}:
3842 "NothingKept"
3843 case ForwardFailed{error}:
3844 ForwardError.show(error)
3845
3846
3847def Sent.carrier(sent: Sent) -> List<&2, Header>:
3848 match sent:
3849 case Fresh{operation, injection}:
3850 Injection.carrier(injection)
3851 case Forwarded{carrier, error}:
3852 carrier
3853 case NoContext{carrier, error, reason}:
3854 carrier
3855
3856
3857def Sent.operation(sent: Sent) -> Maybe<&2, LocalContext>:
3858 match sent:
3859 case Fresh{operation, injection}:
3860 Some{operation}
3861 case Forwarded{carrier, error}:
3862 None{}
3863 case NoContext{carrier, error, reason}:
3864 None{}
3865
3866
3867def Sent.error(sent: Sent) -> Maybe<&2, GenerationError>:
3868 match sent:
3869 case Fresh{operation, injection}:
3870 None{}
3871 case Forwarded{carrier, error}:
3872 Some{error}
3873 case NoContext{carrier, error, reason}:
3874 Some{error}
3875
3876
3877
3878
3879def Sent.dropped(sent: Sent) -> List<&2, StateKey>:
3880 match sent:
3881 case Fresh{operation, injection}:
3882 Injection.dropped(injection)
3883 case Forwarded{carrier, error}:
3884 Nil{}
3885 case NoContext{carrier, error, reason}:
3886 Nil{}
3887
3888
3889
3890def Send.fresh(dropped: List<&2, StateKey>) -> String:
3891 match dropped:
3892 case Nil{}:
3893 "Fresh"
3894 case Con{key, rest}:
3895 "Fresh, truncated"
3896
3897
3898
3899
3900def Sent.show(sent: Sent) -> String:
3901 match sent:
3902 case Fresh{operation, injection}:
3903 Send.fresh(Injection.dropped(injection))
3904 case Forwarded{carrier, error}:
3905 "Forwarded after " ++ GenerationError.show(error)
3906 case NoContext{carrier, error, reason}:
3907 "NoContext after " ++ GenerationError.show(error) ++ ", " ++ Unforwarded.show(reason)