@@ -28,24 +28,201 @@ pub const U32Iterator = struct {
2828 .done = false ,
2929 };
3030 }
31+
32+ // Iterators should be iterable. There's a [JS] example on MDN that
33+ // suggests this is the correct approach:
34+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol
35+ pub fn _symbol_iterator (self : * U32Iterator ) * U32Iterator {
36+ return self ;
37+ }
3138};
3239
33- const testing = std .testing ;
34- test "U32Iterator" {
35- const Return = U32Iterator .Return ;
40+ // A wrapper around an iterator that emits an Iterable result
41+ // An iterable has a next() which emits a {done: bool, value: T} result
42+ pub fn Iterable (comptime T : type , comptime JsName : []const u8 ) type {
43+ // The inner iterator's return type.
44+ // Maybe an error union.
45+ // Definitely an optional
46+ const RawValue = @typeInfo (@TypeOf (T ._next )).@"fn" .return_type .? ;
47+ const CanError = @typeInfo (RawValue ) == .error_union ;
48+
49+ const Value = blk : {
50+ // Unwrap the RawValue
51+ var V = RawValue ;
52+ if (CanError ) {
53+ V = @typeInfo (V ).error_union .payload ;
54+ }
55+ break :blk @typeInfo (V ).optional .child ;
56+ };
57+
58+ const Result = struct {
59+ done : bool ,
60+ // todo, technically, we should return undefined when done = true
61+ // or even omit the value;
62+ value : ? Value ,
63+ };
64+
65+ const ReturnType = if (CanError ) T .Error ! Result else Result ;
66+
67+ return struct {
68+ // the inner value iterator
69+ inner : T ,
70+
71+ // Generics don't generate clean names. Can't just take the resulting
72+ // type name and use that as a the JS class name. So we always ask for
73+ // an explicit JS class name
74+ pub const js_name = JsName ;
75+
76+ const Self = @This ();
77+
78+ pub fn init (inner : T ) Self {
79+ return .{ .inner = inner };
80+ }
81+
82+ pub fn _next (self : * Self ) ReturnType {
83+ const value = if (comptime CanError ) try self .inner ._next () else self .inner ._next ();
84+ return .{ .done = value == null , .value = value };
85+ }
86+
87+ pub fn _symbol_iterator (self : * Self ) * Self {
88+ return self ;
89+ }
90+ };
91+ }
92+
93+ // A wrapper around an iterator that emits integer/index keyed entries.
94+ pub fn NumericEntries (comptime T : type , comptime JsName : []const u8 ) type {
95+ // The inner iterator's return type.
96+ // Maybe an error union.
97+ // Definitely an optional
98+ const RawValue = @typeInfo (@TypeOf (T ._next )).@"fn" .return_type .? ;
99+ const CanError = @typeInfo (RawValue ) == .error_union ;
100+
101+ const Value = blk : {
102+ // Unwrap the RawValue
103+ var V = RawValue ;
104+ if (CanError ) {
105+ V = @typeInfo (V ).error_union .payload ;
106+ }
107+ break :blk @typeInfo (V ).optional .child ;
108+ };
109+
110+ const ReturnType = if (CanError ) T .Error ! ? struct { u32 , Value } else ? struct { u32 , Value };
111+
112+ // Avoid ambiguity. We want to expose a NumericEntries(T).Iterable, so we
113+ // need a declartion inside here for an "Iterable", but that will conflict
114+ // with the above Iterable generic function we have.
115+ const BaseIterable = Iterable ;
116+
117+ return struct {
118+ // the inner value iterator
119+ inner : T ,
120+ index : u32 ,
121+
122+ const Self = @This ();
123+
124+ // Generics don't generate clean names. Can't just take the resulting
125+ // type name and use that as a the JS class name. So we always ask for
126+ // an explicit JS class name
127+ pub const js_name = JsName ;
36128
129+ // re-exposed for when/if we compose this type into an Iterable
130+ pub const Error = T .Error ;
131+
132+ // This iterator as an iterable
133+ pub const Iterable = BaseIterable (Self , JsName ++ "Iterable" );
134+
135+ pub fn init (inner : T ) Self {
136+ return .{ .inner = inner , .index = 0 };
137+ }
138+
139+ pub fn _next (self : * Self ) ReturnType {
140+ const value_ = if (comptime CanError ) try self .inner ._next () else self .inner ._next ();
141+ const value = value_ orelse return null ;
142+
143+ const index = self .index ;
144+ self .index = index + 1 ;
145+ return .{ index , value };
146+ }
147+
148+ // make the iterator, iterable
149+ pub fn _symbol_iterator (self : * Self ) Self.Iterable {
150+ return Self .Iterable .init (self .* );
151+ }
152+ };
153+ }
154+
155+ const testing = @import ("../../testing.zig" );
156+ test "U32Iterator" {
37157 {
38158 var it = U32Iterator { .length = 0 };
39- try testing .expectEqual (Return { .value = 0 , .done = true }, it ._next ());
40- try testing .expectEqual (Return { .value = 0 , .done = true }, it ._next ());
159+ try testing .expectEqual (. { .value = 0 , .done = true }, it ._next ());
160+ try testing .expectEqual (. { .value = 0 , .done = true }, it ._next ());
41161 }
42162
43163 {
44164 var it = U32Iterator { .length = 3 };
45- try testing .expectEqual (Return { .value = 0 , .done = false }, it ._next ());
46- try testing .expectEqual (Return { .value = 1 , .done = false }, it ._next ());
47- try testing .expectEqual (Return { .value = 2 , .done = false }, it ._next ());
48- try testing .expectEqual (Return { .value = 0 , .done = true }, it ._next ());
49- try testing .expectEqual (Return { .value = 0 , .done = true }, it ._next ());
165+ try testing .expectEqual (. { .value = 0 , .done = false }, it ._next ());
166+ try testing .expectEqual (. { .value = 1 , .done = false }, it ._next ());
167+ try testing .expectEqual (. { .value = 2 , .done = false }, it ._next ());
168+ try testing .expectEqual (. { .value = 0 , .done = true }, it ._next ());
169+ try testing .expectEqual (. { .value = 0 , .done = true }, it ._next ());
50170 }
51171}
172+
173+ test "NumericEntries" {
174+ const it = DummyIterator {};
175+ var entries = NumericEntries (DummyIterator , "DummyIterator" ).init (it );
176+
177+ const v1 = entries ._next ().? ;
178+ try testing .expectEqual (0 , v1 .@"0" );
179+ try testing .expectEqual ("it's" , v1 .@"1" );
180+
181+ const v2 = entries ._next ().? ;
182+ try testing .expectEqual (1 , v2 .@"0" );
183+ try testing .expectEqual ("over" , v2 .@"1" );
184+
185+ const v3 = entries ._next ().? ;
186+ try testing .expectEqual (2 , v3 .@"0" );
187+ try testing .expectEqual ("9000!!" , v3 .@"1" );
188+
189+ try testing .expectEqual (null , entries ._next ());
190+ try testing .expectEqual (null , entries ._next ());
191+ try testing .expectEqual (null , entries ._next ());
192+ }
193+
194+ test "Iterable" {
195+ const it = DummyIterator {};
196+ var entries = Iterable (DummyIterator , "DummyIterator" ).init (it );
197+
198+ const v1 = entries ._next ();
199+ try testing .expectEqual (false , v1 .done );
200+ try testing .expectEqual ("it's" , v1 .value .? );
201+
202+ const v2 = entries ._next ();
203+ try testing .expectEqual (false , v2 .done );
204+ try testing .expectEqual ("over" , v2 .value .? );
205+
206+ const v3 = entries ._next ();
207+ try testing .expectEqual (false , v3 .done );
208+ try testing .expectEqual ("9000!!" , v3 .value .? );
209+
210+ try testing .expectEqual (true , entries ._next ().done );
211+ try testing .expectEqual (true , entries ._next ().done );
212+ try testing .expectEqual (true , entries ._next ().done );
213+ }
214+
215+ const DummyIterator = struct {
216+ index : u32 = 0 ,
217+
218+ pub fn _next (self : * DummyIterator ) ? []const u8 {
219+ const index = self .index ;
220+ self .index = index + 1 ;
221+ return switch (index ) {
222+ 0 = > "it's" ,
223+ 1 = > "over" ,
224+ 2 = > "9000!!" ,
225+ else = > null ,
226+ };
227+ }
228+ };
0 commit comments