1use crate::error::BridgeError;
2use crate::traits::{FromCore, ToCore};
3use tidepool_eval::Value;
4use tidepool_repr::{DataConId, DataConTable, Literal};
5
6fn is_boxing_con(name: &str, id: DataConId, table: &DataConTable) -> bool {
8 table
9 .get_by_name(name)
10 .map_or(false, |expected| expected == id)
11}
12
13fn type_mismatch(expected: &str, got: &Value) -> BridgeError {
15 let got_str = match got {
16 Value::Lit(l) => format!("{:?}", l),
17 Value::Con(id, _) => format!("Con({:?})", id),
18 Value::Closure(_, _, _) => "Closure".to_string(),
19 Value::ThunkRef(_) => "ThunkRef".to_string(),
20 Value::JoinCont(_, _, _) => "JoinCont".to_string(),
21 Value::ConFun(id, arity, args) => format!("ConFun({:?}, {}/{})", id, args.len(), arity),
22 };
23 BridgeError::TypeMismatch {
24 expected: expected.to_string(),
25 got: got_str,
26 }
27}
28
29impl<T> FromCore for std::marker::PhantomData<T> {
30 fn from_value(value: &Value, _table: &DataConTable) -> Result<Self, BridgeError> {
31 match value {
32 Value::Con(_, fields) if fields.is_empty() => Ok(std::marker::PhantomData),
33 Value::Con(id, fields) => Err(BridgeError::ArityMismatch {
34 con: *id,
35 expected: 0,
36 got: fields.len(),
37 }),
38 _ => Err(type_mismatch("Con", value)),
39 }
40 }
41}
42
43impl<T> ToCore for std::marker::PhantomData<T> {
44 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
45 let id = table
51 .get_by_name("()")
52 .or_else(|| table.iter().find(|dc| dc.rep_arity == 0).map(|dc| dc.id))
53 .ok_or_else(|| BridgeError::UnknownDataConName("()".into()))?;
54 Ok(Value::Con(id, vec![]))
55 }
56}
57
58impl ToCore for Value {
62 fn to_value(&self, _table: &DataConTable) -> Result<Value, BridgeError> {
63 Ok(self.clone())
64 }
65}
66
67impl FromCore for Value {
68 fn from_value(value: &Value, _table: &DataConTable) -> Result<Self, BridgeError> {
69 Ok(value.clone())
70 }
71}
72
73impl<T: FromCore> FromCore for Box<T> {
74 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
75 T::from_value(value, table).map(Box::new)
76 }
77}
78
79impl<T: ToCore> ToCore for Box<T> {
80 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
81 (**self).to_value(table)
82 }
83}
84
85impl ToCore for () {
88 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
89 match table.get_by_name("()") {
90 Some(id) => Ok(Value::Con(id, vec![])),
91 None => Ok(Value::Lit(Literal::LitInt(0))),
92 }
93 }
94}
95
96impl FromCore for () {
97 fn from_value(value: &Value, _table: &DataConTable) -> Result<Self, BridgeError> {
98 match value {
99 Value::Con(_, fields) if fields.is_empty() => Ok(()),
100 Value::Lit(Literal::LitInt(0)) => Ok(()),
101 _ => Err(type_mismatch("()", value)),
102 }
103 }
104}
105
106impl FromCore for i64 {
111 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
112 match value {
113 Value::Lit(Literal::LitInt(n)) => Ok(*n),
114 Value::Con(id, fields) if fields.len() == 1 => {
115 if is_boxing_con("I#", *id, table) {
116 i64::from_value(&fields[0], table)
117 } else {
118 Err(type_mismatch("LitInt or I#", value))
119 }
120 }
121 _ => Err(type_mismatch("LitInt or I#", value)),
122 }
123 }
124}
125
126impl ToCore for i64 {
127 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
128 match table.get_by_name("I#") {
129 Some(id) => Ok(Value::Con(id, vec![Value::Lit(Literal::LitInt(*self))])),
130 None => Ok(Value::Lit(Literal::LitInt(*self))),
131 }
132 }
133}
134
135impl FromCore for u64 {
138 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
139 match value {
140 Value::Lit(Literal::LitWord(n)) => Ok(*n),
141 Value::Con(id, fields) if fields.len() == 1 => {
142 if is_boxing_con("W#", *id, table) {
143 u64::from_value(&fields[0], table)
144 } else {
145 Err(type_mismatch("LitWord or W#", value))
146 }
147 }
148 _ => Err(type_mismatch("LitWord or W#", value)),
149 }
150 }
151}
152
153impl ToCore for u64 {
154 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
155 match table.get_by_name("W#") {
156 Some(id) => Ok(Value::Con(id, vec![Value::Lit(Literal::LitWord(*self))])),
157 None => Ok(Value::Lit(Literal::LitWord(*self))),
158 }
159 }
160}
161
162impl FromCore for f64 {
165 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
166 match value {
167 Value::Lit(Literal::LitDouble(bits)) => Ok(f64::from_bits(*bits)),
168 Value::Con(id, fields) if fields.len() == 1 => {
169 if is_boxing_con("D#", *id, table) {
170 f64::from_value(&fields[0], table)
171 } else {
172 Err(type_mismatch("LitDouble or D#", value))
173 }
174 }
175 _ => Err(type_mismatch("LitDouble or D#", value)),
176 }
177 }
178}
179
180impl ToCore for f64 {
181 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
182 match table.get_by_name("D#") {
183 Some(id) => Ok(Value::Con(id, vec![Value::Lit(Literal::LitDouble(self.to_bits()))])),
184 None => Ok(Value::Lit(Literal::LitDouble(self.to_bits()))),
185 }
186 }
187}
188
189impl FromCore for i32 {
192 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
193 let n = i64::from_value(value, table)?;
194 if n < i32::MIN as i64 || n > i32::MAX as i64 {
195 return Err(BridgeError::TypeMismatch {
196 expected: "i32 in range [-2147483648, 2147483647]".to_string(),
197 got: format!("LitInt({})", n),
198 });
199 }
200 Ok(n as i32)
201 }
202}
203
204impl ToCore for i32 {
205 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
206 (*self as i64).to_value(table)
207 }
208}
209
210impl FromCore for bool {
212 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
213 match value {
214 Value::Con(id, fields) => {
215 let true_id = table
216 .get_by_name("True")
217 .ok_or(BridgeError::UnknownDataConName("True".into()))?;
218 let false_id = table
219 .get_by_name("False")
220 .ok_or(BridgeError::UnknownDataConName("False".into()))?;
221
222 if *id == true_id {
223 if fields.is_empty() {
224 Ok(true)
225 } else {
226 Err(BridgeError::ArityMismatch {
227 con: *id,
228 expected: 0,
229 got: fields.len(),
230 })
231 }
232 } else if *id == false_id {
233 if fields.is_empty() {
234 Ok(false)
235 } else {
236 Err(BridgeError::ArityMismatch {
237 con: *id,
238 expected: 0,
239 got: fields.len(),
240 })
241 }
242 } else {
243 Err(BridgeError::UnknownDataCon(*id))
244 }
245 }
246 _ => Err(type_mismatch("Con", value)),
247 }
248 }
249}
250
251impl ToCore for bool {
252 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
253 let name = if *self { "True" } else { "False" };
254 let id = table
255 .get_by_name(name)
256 .ok_or_else(|| BridgeError::UnknownDataConName(name.into()))?;
257 Ok(Value::Con(id, vec![]))
258 }
259}
260
261impl FromCore for char {
263 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
264 match value {
265 Value::Lit(Literal::LitChar(c)) => Ok(*c),
266 Value::Con(id, fields) if fields.len() == 1 => {
267 if is_boxing_con("C#", *id, table) {
268 char::from_value(&fields[0], table)
269 } else {
270 Err(type_mismatch("LitChar or C#", value))
271 }
272 }
273 _ => Err(type_mismatch("LitChar or C#", value)),
274 }
275 }
276}
277
278impl ToCore for char {
279 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
280 match table.get_by_name("C#") {
281 Some(id) => Ok(Value::Con(id, vec![Value::Lit(Literal::LitChar(*self))])),
282 None => Ok(Value::Lit(Literal::LitChar(*self))),
283 }
284 }
285}
286
287impl FromCore for String {
288 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
289 match value {
290 Value::Lit(Literal::LitString(bytes)) => {
291 String::from_utf8(bytes.clone()).map_err(|e| BridgeError::TypeMismatch {
292 expected: "UTF-8 String".to_string(),
293 got: format!("Invalid UTF-8: {}", e),
294 })
295 }
296 Value::Con(_, _) => {
298 let mut chars = Vec::new();
299 let mut cur = value;
300 loop {
301 match cur {
302 Value::Con(tag, fields) if table.get_by_name("[]").map_or(false, |nil| nil == *tag) && fields.is_empty() => {
303 break;
304 }
305 Value::Con(tag, fields) if table.get_by_name(":").map_or(false, |cons| cons == *tag) && fields.len() == 2 => {
306 match &fields[0] {
307 Value::Lit(Literal::LitChar(c)) => chars.push(*c),
308 Value::Con(box_tag, box_fields) if table.get_by_name("C#").map_or(false, |c_id| c_id == *box_tag) && box_fields.len() == 1 => {
310 match &box_fields[0] {
311 Value::Lit(Literal::LitChar(c)) => chars.push(*c),
312 other => return Err(type_mismatch("Char in C#", other)),
313 }
314 }
315 other => return Err(type_mismatch("Char or C#", other)),
316 }
317 cur = &fields[1];
318 }
319 _ => return Err(type_mismatch("[] or (:)", cur)),
320 }
321 }
322 Ok(chars.into_iter().collect())
323 }
324 _ => Err(type_mismatch("LitString or [Char]", value)),
325 }
326 }
327}
328
329impl ToCore for String {
330 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
331 let cons_id = table.get_by_name(":").ok_or_else(|| BridgeError::UnknownDataConName("(:)".into()))?;
332 let nil_id = table.get_by_name("[]").ok_or_else(|| BridgeError::UnknownDataConName("[]".into()))?;
333 let mut result = Value::Con(nil_id, vec![]);
334 for ch in self.chars().rev() {
335 let char_val = Value::Lit(Literal::LitChar(ch));
336 result = Value::Con(cons_id, vec![char_val, result]);
337 }
338 Ok(result)
339 }
340}
341
342impl<T: FromCore> FromCore for Option<T> {
345 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
346 match value {
347 Value::Con(id, fields) => {
348 let nothing_id = table
349 .get_by_name("Nothing")
350 .ok_or(BridgeError::UnknownDataConName("Nothing".into()))?;
351 let just_id = table
352 .get_by_name("Just")
353 .ok_or(BridgeError::UnknownDataConName("Just".into()))?;
354
355 if *id == nothing_id {
356 if fields.is_empty() {
357 Ok(None)
358 } else {
359 Err(BridgeError::ArityMismatch {
360 con: *id,
361 expected: 0,
362 got: fields.len(),
363 })
364 }
365 } else if *id == just_id {
366 if fields.len() == 1 {
367 Ok(Some(T::from_value(&fields[0], table)?))
368 } else {
369 Err(BridgeError::ArityMismatch {
370 con: *id,
371 expected: 1,
372 got: fields.len(),
373 })
374 }
375 } else {
376 Err(BridgeError::UnknownDataCon(*id))
377 }
378 }
379 _ => Err(type_mismatch("Con", value)),
380 }
381 }
382}
383
384impl<T: ToCore> ToCore for Option<T> {
385 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
386 match self {
387 None => {
388 let id = table
389 .get_by_name("Nothing")
390 .ok_or_else(|| BridgeError::UnknownDataConName("Nothing".into()))?;
391 Ok(Value::Con(id, vec![]))
392 }
393 Some(x) => {
394 let id = table
395 .get_by_name("Just")
396 .ok_or_else(|| BridgeError::UnknownDataConName("Just".into()))?;
397 Ok(Value::Con(id, vec![x.to_value(table)?]))
398 }
399 }
400 }
401}
402
403impl<T: FromCore> FromCore for Vec<T> {
404 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
405 let nil_id = table
406 .get_by_name("[]")
407 .ok_or(BridgeError::UnknownDataConName("[]".into()))?;
408 let cons_id = table
409 .get_by_name(":")
410 .ok_or(BridgeError::UnknownDataConName(":".into()))?;
411
412 let mut res = Vec::new();
413 let mut curr = value;
414
415 loop {
416 match curr {
417 Value::Con(id, fields) => {
418 if *id == nil_id {
419 if fields.is_empty() {
420 break;
421 } else {
422 return Err(BridgeError::ArityMismatch {
423 con: *id,
424 expected: 0,
425 got: fields.len(),
426 });
427 }
428 } else if *id == cons_id {
429 if fields.len() == 2 {
430 res.push(T::from_value(&fields[0], table)?);
431 curr = &fields[1];
432 } else {
433 return Err(BridgeError::ArityMismatch {
434 con: *id,
435 expected: 2,
436 got: fields.len(),
437 });
438 }
439 } else {
440 return Err(BridgeError::UnknownDataCon(*id));
441 }
442 }
443 _ => return Err(type_mismatch("Con", curr)),
444 }
445 }
446
447 Ok(res)
448 }
449}
450
451impl<T: ToCore> ToCore for Vec<T> {
452 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
453 let nil_id = table
454 .get_by_name("[]")
455 .ok_or_else(|| BridgeError::UnknownDataConName("[]".into()))?;
456 let cons_id = table
457 .get_by_name(":")
458 .ok_or_else(|| BridgeError::UnknownDataConName(":".into()))?;
459
460 let mut res = Value::Con(nil_id, vec![]);
461 for x in self.iter().rev() {
462 res = Value::Con(cons_id, vec![x.to_value(table)?, res]);
463 }
464 Ok(res)
465 }
466}
467
468impl<T: FromCore, E: FromCore> FromCore for Result<T, E> {
469 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
470 match value {
471 Value::Con(id, fields) => {
472 let right_id = table
473 .get_by_name("Right")
474 .or_else(|| table.get_by_name("Ok"))
475 .ok_or(BridgeError::UnknownDataConName("Right/Ok".into()))?;
476 let left_id = table
477 .get_by_name("Left")
478 .or_else(|| table.get_by_name("Err"))
479 .ok_or(BridgeError::UnknownDataConName("Left/Err".into()))?;
480
481 if *id == right_id {
482 if fields.len() == 1 {
483 Ok(Ok(T::from_value(&fields[0], table)?))
484 } else {
485 Err(BridgeError::ArityMismatch {
486 con: *id,
487 expected: 1,
488 got: fields.len(),
489 })
490 }
491 } else if *id == left_id {
492 if fields.len() == 1 {
493 Ok(Err(E::from_value(&fields[0], table)?))
494 } else {
495 Err(BridgeError::ArityMismatch {
496 con: *id,
497 expected: 1,
498 got: fields.len(),
499 })
500 }
501 } else {
502 Err(BridgeError::UnknownDataCon(*id))
503 }
504 }
505 _ => Err(type_mismatch("Con", value)),
506 }
507 }
508}
509
510impl<T: ToCore, E: ToCore> ToCore for Result<T, E> {
511 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
512 match self {
513 Ok(x) => {
514 let id = table
515 .get_by_name("Right")
516 .or_else(|| table.get_by_name("Ok"))
517 .ok_or_else(|| BridgeError::UnknownDataConName("Right/Ok".into()))?;
518 Ok(Value::Con(id, vec![x.to_value(table)?]))
519 }
520 Err(e) => {
521 let id = table
522 .get_by_name("Left")
523 .or_else(|| table.get_by_name("Err"))
524 .ok_or_else(|| BridgeError::UnknownDataConName("Left/Err".into()))?;
525 Ok(Value::Con(id, vec![e.to_value(table)?]))
526 }
527 }
528 }
529}
530
531impl<A: FromCore, B: FromCore> FromCore for (A, B) {
534 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
535 match value {
536 Value::Con(id, fields) => {
537 let pair_id = table
538 .get_by_name("(,)")
539 .ok_or(BridgeError::UnknownDataConName("(,)".into()))?;
540 if *id == pair_id {
541 if fields.len() == 2 {
542 Ok((
543 A::from_value(&fields[0], table)?,
544 B::from_value(&fields[1], table)?,
545 ))
546 } else {
547 Err(BridgeError::ArityMismatch {
548 con: *id,
549 expected: 2,
550 got: fields.len(),
551 })
552 }
553 } else {
554 Err(BridgeError::UnknownDataCon(*id))
555 }
556 }
557 _ => Err(type_mismatch("Con", value)),
558 }
559 }
560}
561
562impl<A: ToCore, B: ToCore> ToCore for (A, B) {
563 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
564 let pair_id = table
565 .get_by_name("(,)")
566 .ok_or_else(|| BridgeError::UnknownDataConName("(,)".into()))?;
567 Ok(Value::Con(
568 pair_id,
569 vec![self.0.to_value(table)?, self.1.to_value(table)?],
570 ))
571 }
572}
573
574impl<A: FromCore, B: FromCore, C: FromCore> FromCore for (A, B, C) {
575 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
576 match value {
577 Value::Con(id, fields) => {
578 let triple_id = table
579 .get_by_name("(,,)")
580 .ok_or(BridgeError::UnknownDataConName("(,,)".into()))?;
581 if *id == triple_id {
582 if fields.len() == 3 {
583 Ok((
584 A::from_value(&fields[0], table)?,
585 B::from_value(&fields[1], table)?,
586 C::from_value(&fields[2], table)?,
587 ))
588 } else {
589 Err(BridgeError::ArityMismatch {
590 con: *id,
591 expected: 3,
592 got: fields.len(),
593 })
594 }
595 } else {
596 Err(BridgeError::UnknownDataCon(*id))
597 }
598 }
599 _ => Err(type_mismatch("Con", value)),
600 }
601 }
602}
603
604impl<A: ToCore, B: ToCore, C: ToCore> ToCore for (A, B, C) {
605 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
606 let triple_id = table
607 .get_by_name("(,,)")
608 .ok_or_else(|| BridgeError::UnknownDataConName("(,,)".into()))?;
609 Ok(Value::Con(
610 triple_id,
611 vec![
612 self.0.to_value(table)?,
613 self.1.to_value(table)?,
614 self.2.to_value(table)?,
615 ],
616 ))
617 }
618}
619
620#[cfg(test)]
621mod tests {
622 use super::*;
623 use tidepool_repr::{DataCon, DataConId};
624
625 fn test_table() -> DataConTable {
626 let mut t = DataConTable::new();
627 t.insert(DataCon {
629 id: DataConId(0),
630 name: "Nothing".into(),
631 tag: 1,
632 rep_arity: 0,
633 field_bangs: vec![],
634 });
635 t.insert(DataCon {
636 id: DataConId(1),
637 name: "Just".into(),
638 tag: 2,
639 rep_arity: 1,
640 field_bangs: vec![],
641 });
642 t.insert(DataCon {
643 id: DataConId(2),
644 name: "False".into(),
645 tag: 1,
646 rep_arity: 0,
647 field_bangs: vec![],
648 });
649 t.insert(DataCon {
650 id: DataConId(3),
651 name: "True".into(),
652 tag: 2,
653 rep_arity: 0,
654 field_bangs: vec![],
655 });
656 t.insert(DataCon {
657 id: DataConId(4),
658 name: "(,)".into(),
659 tag: 1,
660 rep_arity: 2,
661 field_bangs: vec![],
662 });
663 t.insert(DataCon {
664 id: DataConId(5),
665 name: "[]".into(),
666 tag: 1,
667 rep_arity: 0,
668 field_bangs: vec![],
669 });
670 t.insert(DataCon {
671 id: DataConId(6),
672 name: ":".into(),
673 tag: 2,
674 rep_arity: 2,
675 field_bangs: vec![],
676 });
677 t.insert(DataCon {
678 id: DataConId(7),
679 name: "(,,)".into(),
680 tag: 1,
681 rep_arity: 3,
682 field_bangs: vec![],
683 });
684 t.insert(DataCon {
685 id: DataConId(8),
686 name: "Right".into(),
687 tag: 2,
688 rep_arity: 1,
689 field_bangs: vec![],
690 });
691 t.insert(DataCon {
692 id: DataConId(9),
693 name: "Left".into(),
694 tag: 1,
695 rep_arity: 1,
696 field_bangs: vec![],
697 });
698 t
699 }
700
701 fn roundtrip<T: FromCore + ToCore + PartialEq + std::fmt::Debug>(val: T, table: &DataConTable) {
702 let value = val.to_value(table).expect("ToValue failed");
703 let back = T::from_value(&value, table).expect("FromValue failed");
704 assert_eq!(val, back);
705 }
706
707 #[test]
708 fn test_i64_roundtrip() {
709 let table = test_table();
710 roundtrip(42i64, &table);
711 roundtrip(-7i64, &table);
712 }
713
714 #[test]
715 fn test_i32_roundtrip() {
716 let table = test_table();
717 roundtrip(42i32, &table);
718 roundtrip(-7i32, &table);
719 }
720
721 #[test]
722 fn test_i32_overflow() {
723 let table = test_table();
724 let val: i64 = i32::MAX as i64 + 1;
725 let value = val.to_value(&table).unwrap();
726 let res = i32::from_value(&value, &table);
727 assert!(matches!(res, Err(BridgeError::TypeMismatch { .. })));
728 }
729
730 #[test]
731 fn test_u64_roundtrip() {
732 let table = test_table();
733 roundtrip(42u64, &table);
734 }
735
736 #[test]
737 fn test_f64_roundtrip() {
738 let table = test_table();
739 roundtrip(3.14159f64, &table);
740 roundtrip(-0.0f64, &table);
741 }
742
743 #[test]
744 fn test_bool_roundtrip() {
745 let table = test_table();
746 roundtrip(true, &table);
747 roundtrip(false, &table);
748 }
749
750 #[test]
751 fn test_char_roundtrip() {
752 let table = test_table();
753 roundtrip('a', &table);
754 roundtrip('λ', &table);
755 }
756
757 #[test]
758 fn test_string_roundtrip() {
759 let table = test_table();
760 roundtrip("hello".to_string(), &table);
761 roundtrip("".to_string(), &table);
762 }
763
764 #[test]
765 fn test_option_roundtrip() {
766 let table = test_table();
767 roundtrip(Some(42i64), &table);
768 roundtrip(None::<i64>, &table);
769 }
770
771 #[test]
772 fn test_vec_roundtrip() {
773 let table = test_table();
774 roundtrip(vec![1i64, 2, 3], &table);
775 roundtrip(Vec::<i64>::new(), &table);
776 }
777
778 #[test]
779 fn test_result_roundtrip() {
780 let table = test_table();
781 roundtrip(Ok::<i64, String>(42), &table);
782 roundtrip(Err::<i64, String>("error".to_string()), &table);
783 }
784
785 #[test]
786 fn test_tuple2_roundtrip() {
787 let table = test_table();
788 roundtrip((42i64, true), &table);
789 }
790
791 #[test]
792 fn test_tuple3_roundtrip() {
793 let table = test_table();
794 roundtrip((42i64, true, "hello".to_string()), &table);
795 }
796
797 #[test]
798 fn test_nested_roundtrip() {
799 let table = test_table();
800 roundtrip(vec![Some(1i64), None, Some(3)], &table);
801 roundtrip(Some((42i64, vec![true, false])), &table);
802 }
803
804 #[test]
805 fn test_unknown_datacon() {
806 let table = test_table();
807 let value = Value::Con(DataConId(100), vec![]);
808 let res = bool::from_value(&value, &table);
809 assert!(matches!(
810 res,
811 Err(BridgeError::UnknownDataCon(DataConId(100)))
812 ));
813 }
814
815 #[test]
816 fn test_arity_mismatch() {
817 let table = test_table();
818 let true_id = table.get_by_name("True").unwrap();
819 let value = Value::Con(true_id, vec![Value::Lit(Literal::LitInt(1))]);
820 let res = bool::from_value(&value, &table);
821 assert!(matches!(res, Err(BridgeError::ArityMismatch { .. })));
822 }
823
824 #[test]
825 fn test_type_mismatch() {
826 let table = test_table();
827 let value = Value::Lit(Literal::LitInt(1));
828 let res = bool::from_value(&value, &table);
829 assert!(matches!(res, Err(BridgeError::TypeMismatch { .. })));
830 }
831}