Fork of daniellemaywood.uk/gleam — Wasm codegen work
9.4 kB
469 lines
1use crate::assert_erl;
2use crate::erlang::*;
3use crate::type_;
4
5#[test]
6fn basic() {
7 insta::assert_snapshot!(record_definition(
8 "PetCat",
9 &[
10 ("name", type_::tuple(vec![])),
11 ("is_cute", type_::tuple(vec![]))
12 ]
13 ));
14}
15
16#[test]
17fn reserve_words() {
18 // Reserved words are escaped in record names and fields
19 insta::assert_snapshot!(record_definition(
20 "div",
21 &[
22 ("receive", type_::int()),
23 ("catch", type_::tuple(vec![])),
24 ("unreserved", type_::tuple(vec![]))
25 ]
26 ));
27}
28
29#[test]
30fn type_vars() {
31 // Type vars are printed as `any()` because records don't support generics
32 insta::assert_snapshot!(record_definition(
33 "PetCat",
34 &[
35 ("name", type_::generic_var(1)),
36 ("is_cute", type_::unbound_var(1)),
37 ("linked", type_::link(type_::int()))
38 ]
39 ));
40}
41
42#[test]
43fn module_types() {
44 // Types are printed with module qualifiers
45 let module_name = "name".into();
46 insta::assert_snapshot!(record_definition(
47 "PetCat",
48 &[(
49 "name",
50 Arc::new(Type::Named {
51 publicity: Publicity::Public,
52 package: "package".into(),
53 module: module_name,
54 name: "my_type".into(),
55 arguments: vec![],
56 inferred_variant: None,
57 })
58 )]
59 ));
60}
61
62#[test]
63fn long_definition_formatting() {
64 // Long definition formatting
65 insta::assert_snapshot!(record_definition(
66 "PetCat",
67 &[
68 ("name", type_::generic_var(1)),
69 ("is_cute", type_::unbound_var(1)),
70 ("linked", type_::link(type_::int())),
71 (
72 "whatever",
73 type_::list(type_::tuple(vec![
74 type_::nil(),
75 type_::list(type_::tuple(vec![type_::nil(), type_::nil(), type_::nil()])),
76 type_::nil(),
77 type_::list(type_::tuple(vec![type_::nil(), type_::nil(), type_::nil()])),
78 type_::nil(),
79 type_::list(type_::tuple(vec![type_::nil(), type_::nil(), type_::nil()])),
80 ]))
81 ),
82 ]
83 ));
84}
85
86#[test]
87fn record_accessors() {
88 // We can use record accessors for types with only one constructor
89 assert_erl!(
90 r#"
91pub type Person { Person(name: String, age: Int) }
92pub fn get_age(person: Person) { person.age }
93pub fn get_name(person: Person) { person.name }
94"#
95 );
96}
97
98#[test]
99fn record_accessor_multiple_variants() {
100 // We can access fields on custom types with multiple variants
101 assert_erl!(
102 "
103pub type Person {
104 Teacher(name: String, title: String)
105 Student(name: String, age: Int)
106}
107pub fn get_name(person: Person) { person.name }"
108 );
109}
110
111#[test]
112fn record_accessor_multiple_variants_positions_other_than_first() {
113 // We can access fields on custom types with multiple variants
114 // In positions other than the 1st field
115 assert_erl!(
116 "
117pub type Person {
118 Teacher(name: String, age: Int, title: String)
119 Student(name: String, age: Int)
120}
121pub fn get_name(person: Person) { person.name }
122pub fn get_age(person: Person) { person.age }"
123 );
124}
125
126#[test]
127fn record_accessor_multiple_variants_parameterised_types() {
128 // We can access fields on custom types with multiple variants
129 // In positions other than the 1st field
130 assert_erl!(
131 "
132pub type Person {
133 Teacher(name: String, age: List(Int), title: String)
134 Student(name: String, age: List(Int))
135}
136pub fn get_name(person: Person) { person.name }
137pub fn get_age(person: Person) { person.age }"
138 );
139}
140
141#[test]
142fn record_accessor_multiple_with_first_position_different_types() {
143 // We can access fields on custom types with multiple variants
144 // In positions other than the 1st field
145 assert_erl!(
146 "
147pub type Person {
148 Teacher(name: Nil, age: Int)
149 Student(name: String, age: Int)
150}
151pub fn get_age(person: Person) { person.age }"
152 );
153}
154
155#[test]
156fn record_spread() {
157 // Test binding to a record field with the spread operator
158 assert_erl!(
159 r#"
160pub type Triple {
161 Triple(a: Int, b: Int, c: Int)
162}
163
164pub fn main() {
165 let triple = Triple(1,2,3)
166 let Triple(the_a, ..) = triple
167 the_a
168}
169"#
170 );
171}
172
173#[test]
174fn record_spread1() {
175 // Test binding to a record field with the spread operator
176 // Test binding to a record field with the spread operator and a labelled argument
177 assert_erl!(
178 r#"
179pub type Triple {
180 Triple(a: Int, b: Int, c: Int)
181}
182
183pub fn main() {
184 let triple = Triple(1,2,3)
185 let Triple(b: the_b, ..) = triple
186 the_b
187}
188"#
189 );
190}
191
192#[test]
193fn record_spread2() {
194 // Test binding to a record field with the spread operator with both a labelled argument and a positional argument
195 assert_erl!(
196 r#"
197pub type Triple {
198 Triple(a: Int, b: Int, c: Int)
199}
200
201pub fn main() {
202 let triple = Triple(1,2,3)
203 let Triple(the_a, c: the_c, ..) = triple
204 the_c
205}
206"#
207 );
208}
209
210#[test]
211fn record_spread3() {
212 // Test binding to a record field with the spread operator in a match
213 assert_erl!(
214 r#"
215pub type Triple {
216 Triple(a: Int, b: Int, c: Int)
217}
218
219pub fn main() {
220 let triple = Triple(1,2,3)
221 case triple {
222 Triple(b: the_b, ..) -> the_b
223 }
224}
225"#
226 );
227}
228
229#[test]
230fn record_updates() {
231 // Record updates
232 assert_erl!(
233 r#"
234pub type Person { Person(name: String, age: Int) }
235
236pub fn main() {
237 let p = Person("Quinn", 27)
238 let new_p = Person(..p, age: 28)
239 new_p
240}
241"#
242 );
243}
244
245#[test]
246fn record_updates1() {
247 // Record updates with field accesses
248 assert_erl!(
249 r#"
250pub type Person { Person(name: String, age: Int) }
251
252pub fn main() {
253 let p = Person("Quinn", 27)
254 let new_p = Person(..p, age: p.age + 1)
255 new_p
256}
257"#
258 );
259}
260
261#[test]
262fn record_updates2() {
263 // Record updates with multiple fields
264 assert_erl!(
265 r#"
266pub type Person { Person(name: String, age: Int) }
267
268pub fn main() {
269 let p = Person("Quinn", 27)
270 let new_p = Person(..p, age: 28, name: "Riley")
271 new_p
272}
273"#
274 );
275}
276
277#[test]
278fn record_updates3() {
279 // Record updates when record is returned from function
280 assert_erl!(
281 r#"
282pub type Person { Person(name: String, age: Int) }
283
284pub fn main() {
285 let new_p = Person(..return_person(), age: 28)
286 new_p
287}
288
289fn return_person() {
290 Person("Quinn", 27)
291}
292"#
293 );
294}
295
296#[test]
297fn record_updates4() {
298 // Record updates when record is field on another record
299 assert_erl!(
300 r#"
301pub type Car { Car(make: String, model: String, driver: Person) }
302pub type Person { Person(name: String, age: Int) }
303
304pub fn main() {
305 let car = Car(make: "Amphicar", model: "Model 770", driver: Person(name: "John Doe", age: 27))
306 let new_p = Person(..car.driver, age: 28)
307 new_p
308}
309"#
310 );
311}
312
313#[test]
314fn record_constants() {
315 assert_erl!(
316 "pub type Test { A }
317const some_test = A
318pub fn a() { A }"
319 );
320}
321
322// https://github.com/gleam-lang/gleam/issues/1698
323#[test]
324fn pipe_update_subject() {
325 assert_erl!(
326 "pub type Thing {
327 Thing(a: Int, b: Int)
328}
329
330pub fn identity(x) { x }
331
332pub fn main() {
333 let thing = Thing(1, 2)
334 Thing(..thing |> identity, b: 1000)
335}"
336 );
337}
338
339// https://github.com/gleam-lang/gleam/issues/1698
340#[test]
341fn record_access_block() {
342 assert_erl!(
343 "pub type Thing {
344 Thing(a: Int, b: Int)
345}
346
347pub fn main() {
348 {
349 let thing = Thing(1, 2)
350 thing
351 }.a
352}"
353 );
354}
355
356// https://github.com/gleam-lang/gleam/issues/1981
357#[test]
358fn imported_qualified_constructor_as_fn_name_escape() {
359 assert_erl!(
360 ("other_package", "other_module", "pub type Let { Let(Int) }"),
361 "import other_module
362
363pub fn main() {
364 other_module.Let
365}"
366 );
367}
368
369#[test]
370fn nested_record_update() {
371 assert_erl!(
372 "pub type Wibble {
373 Wibble(a: Int, b: Wobble, c: Int)
374}
375
376pub type Wobble {
377 Wobble(a: Int, b: Int)
378}
379
380pub fn main() {
381 let base = Wibble(1, Wobble(2, 3), 4)
382 Wibble(..base, b: Wobble(..base.b, b: 5))
383}"
384 );
385}
386
387#[test]
388fn nested_record_update_with_blocks() {
389 assert_erl!(
390 "pub type A { A(b: B) }
391pub type B { B(c: C) }
392pub type C { C(val: Int) }
393
394pub fn main(a: A) {
395 A(..a, b: {
396 B(..a.b, c: {
397 C(..a.b.c, val: 0)
398 })
399 })
400}"
401 )
402}
403
404#[test]
405fn private_unused_records() {
406 assert_erl!(
407 "type A { A(inner: Int) }
408type B { B(String) }
409type C { C(Int) }
410
411pub fn main(x: Int) -> Int {
412 let a = A(x)
413 a.inner
414}
415"
416 )
417}
418
419#[test]
420fn const_record_update_generic_respecialization() {
421 assert_erl!(
422 "
423pub type Box(a) {
424 Box(name: String, value: a)
425}
426
427pub const base = Box(\"score\", 50)
428pub const updated = Box(..base, value: \"Hello\")
429
430pub fn main() {
431 #(base, updated)
432}
433",
434 );
435}
436
437#[test]
438fn record_update_with_unlabelled_fields() {
439 assert_erl!(
440 r#"
441pub type Wibble {
442 Wibble(Int, Float, b: Bool, s: String)
443}
444
445pub fn main() {
446 let record = Wibble(1, 3.14, True, "Hello")
447 Wibble(..record, b: False)
448}
449"#
450 );
451}
452
453#[test]
454fn constant_record_update_with_unlabelled_fields() {
455 assert_erl!(
456 r#"
457pub type Wibble {
458 Wibble(Int, Float, b: Bool, s: String)
459}
460
461pub const record = Wibble(1, 3.14, True, "Hello")
462pub const updated = Wibble(..record, b: False)
463
464pub fn main() {
465 updated
466}
467"#
468 );
469}