1use crate::error::BridgeError;
2use crate::traits::{FromCore, ToCore};
3use std::sync::{Arc, Mutex};
4use tidepool_eval::Value;
5use tidepool_repr::{DataConId, DataConTable, Literal};
6
7fn is_boxing_con(name: &str, id: DataConId, table: &DataConTable) -> bool {
9 table.get_by_name(name) == Some(id)
10}
11
12fn type_mismatch(expected: &str, got: &Value) -> BridgeError {
14 let got_str = match got {
15 Value::Lit(l) => format!("{:?}", l),
16 Value::Con(id, _) => format!("Con({:?})", id),
17 Value::Closure(_, _, _) => "Closure".to_string(),
18 Value::ThunkRef(_) => "ThunkRef".to_string(),
19 Value::JoinCont(_, _, _) => "JoinCont".to_string(),
20 Value::ConFun(id, arity, args) => format!("ConFun({:?}, {}/{})", id, args.len(), arity),
21 Value::ByteArray(bs) => match bs.lock() {
22 Ok(b) => format!("ByteArray(len={})", b.len()),
23 Err(_) => "ByteArray(poisoned)".to_string(),
24 },
25 };
26 BridgeError::TypeMismatch {
27 expected: expected.to_string(),
28 got: got_str,
29 }
30}
31
32impl<T> FromCore for std::marker::PhantomData<T> {
33 fn from_value(value: &Value, _table: &DataConTable) -> Result<Self, BridgeError> {
34 match value {
35 Value::Con(_, fields) if fields.is_empty() => Ok(std::marker::PhantomData),
36 Value::Con(id, fields) => Err(BridgeError::ArityMismatch {
37 con: *id,
38 expected: 0,
39 got: fields.len(),
40 }),
41 _ => Err(type_mismatch("Con", value)),
42 }
43 }
44}
45
46impl<T> ToCore for std::marker::PhantomData<T> {
47 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
48 let id = table
54 .get_by_name("()")
55 .or_else(|| table.iter().find(|dc| dc.rep_arity == 0).map(|dc| dc.id))
56 .ok_or_else(|| BridgeError::UnknownDataConName("()".into()))?;
57 Ok(Value::Con(id, vec![]))
58 }
59}
60
61impl ToCore for Value {
65 fn to_value(&self, _table: &DataConTable) -> Result<Value, BridgeError> {
66 Ok(self.clone())
67 }
68}
69
70impl FromCore for Value {
71 fn from_value(value: &Value, _table: &DataConTable) -> Result<Self, BridgeError> {
72 Ok(value.clone())
73 }
74}
75
76impl<T: FromCore> FromCore for Box<T> {
77 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
78 T::from_value(value, table).map(Box::new)
79 }
80}
81
82impl<T: ToCore> ToCore for Box<T> {
83 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
84 (**self).to_value(table)
85 }
86}
87
88impl ToCore for () {
91 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
92 let id = table
93 .get_by_name("()")
94 .ok_or_else(|| BridgeError::UnknownDataConName("()".into()))?;
95 Ok(Value::Con(id, vec![]))
96 }
97}
98
99impl FromCore for () {
100 fn from_value(value: &Value, _table: &DataConTable) -> Result<Self, BridgeError> {
101 match value {
102 Value::Con(_, fields) if fields.is_empty() => Ok(()),
103 _ => Err(type_mismatch("()", value)),
104 }
105 }
106}
107
108impl FromCore for i64 {
113 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
114 match value {
115 Value::Lit(Literal::LitInt(n)) => Ok(*n),
116 Value::Con(id, fields) if fields.len() == 1 => {
117 if is_boxing_con("I#", *id, table) {
118 i64::from_value(&fields[0], table)
119 } else {
120 Err(type_mismatch("LitInt or I#", value))
121 }
122 }
123 _ => Err(type_mismatch("LitInt or I#", value)),
124 }
125 }
126}
127
128impl ToCore for i64 {
129 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
130 let id = table
131 .get_by_name("I#")
132 .ok_or_else(|| BridgeError::UnknownDataConName("I#".into()))?;
133 Ok(Value::Con(id, vec![Value::Lit(Literal::LitInt(*self))]))
134 }
135}
136
137impl FromCore for u64 {
140 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
141 match value {
142 Value::Lit(Literal::LitWord(n)) => Ok(*n),
143 Value::Con(id, fields) if fields.len() == 1 => {
144 if is_boxing_con("W#", *id, table) {
145 u64::from_value(&fields[0], table)
146 } else {
147 Err(type_mismatch("LitWord or W#", value))
148 }
149 }
150 _ => Err(type_mismatch("LitWord or W#", value)),
151 }
152 }
153}
154
155impl ToCore for u64 {
156 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
157 let id = table
158 .get_by_name("W#")
159 .ok_or_else(|| BridgeError::UnknownDataConName("W#".into()))?;
160 Ok(Value::Con(id, vec![Value::Lit(Literal::LitWord(*self))]))
161 }
162}
163
164impl FromCore for f64 {
167 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
168 match value {
169 Value::Lit(Literal::LitDouble(bits)) => Ok(f64::from_bits(*bits)),
170 Value::Con(id, fields) if fields.len() == 1 => {
171 if is_boxing_con("D#", *id, table) {
172 f64::from_value(&fields[0], table)
173 } else {
174 Err(type_mismatch("LitDouble or D#", value))
175 }
176 }
177 _ => Err(type_mismatch("LitDouble or D#", value)),
178 }
179 }
180}
181
182impl ToCore for f64 {
183 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
184 let id = table
185 .get_by_name("D#")
186 .ok_or_else(|| BridgeError::UnknownDataConName("D#".into()))?;
187 Ok(Value::Con(
188 id,
189 vec![Value::Lit(Literal::LitDouble(self.to_bits()))],
190 ))
191 }
192}
193
194impl FromCore for i32 {
197 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
198 let n = i64::from_value(value, table)?;
199 if n < i32::MIN as i64 || n > i32::MAX as i64 {
200 return Err(BridgeError::TypeMismatch {
201 expected: "i32 in range [-2147483648, 2147483647]".to_string(),
202 got: format!("LitInt({})", n),
203 });
204 }
205 Ok(n as i32)
206 }
207}
208
209impl ToCore for i32 {
210 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
211 (*self as i64).to_value(table)
212 }
213}
214
215impl FromCore for bool {
217 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
218 match value {
219 Value::Con(id, fields) => {
220 let true_id = table
221 .get_by_name("True")
222 .ok_or(BridgeError::UnknownDataConName("True".into()))?;
223 let false_id = table
224 .get_by_name("False")
225 .ok_or(BridgeError::UnknownDataConName("False".into()))?;
226
227 if *id == true_id {
228 if fields.is_empty() {
229 Ok(true)
230 } else {
231 Err(BridgeError::ArityMismatch {
232 con: *id,
233 expected: 0,
234 got: fields.len(),
235 })
236 }
237 } else if *id == false_id {
238 if fields.is_empty() {
239 Ok(false)
240 } else {
241 Err(BridgeError::ArityMismatch {
242 con: *id,
243 expected: 0,
244 got: fields.len(),
245 })
246 }
247 } else {
248 Err(BridgeError::UnknownDataCon(*id))
249 }
250 }
251 _ => Err(type_mismatch("Con", value)),
252 }
253 }
254}
255
256impl ToCore for bool {
257 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
258 let name = if *self { "True" } else { "False" };
259 let id = table
260 .get_by_name(name)
261 .ok_or_else(|| BridgeError::UnknownDataConName(name.into()))?;
262 Ok(Value::Con(id, vec![]))
263 }
264}
265
266impl FromCore for char {
268 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
269 match value {
270 Value::Lit(Literal::LitChar(c)) => Ok(*c),
271 Value::Con(id, fields) if fields.len() == 1 => {
272 if is_boxing_con("C#", *id, table) {
273 char::from_value(&fields[0], table)
274 } else {
275 Err(type_mismatch("LitChar or C#", value))
276 }
277 }
278 _ => Err(type_mismatch("LitChar or C#", value)),
279 }
280 }
281}
282
283impl ToCore for char {
284 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
285 let id = table
286 .get_by_name("C#")
287 .ok_or_else(|| BridgeError::UnknownDataConName("C#".into()))?;
288 Ok(Value::Con(id, vec![Value::Lit(Literal::LitChar(*self))]))
289 }
290}
291
292impl FromCore for String {
293 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
294 match value {
295 Value::Con(id, fields)
297 if fields.len() == 3 && table.get_by_name("Text") == Some(*id) =>
298 {
299 let ba = match &fields[0] {
300 Value::ByteArray(bs) => bs
301 .lock()
302 .map_err(|_| BridgeError::InternalError("mutex poisoned".into()))?
303 .clone(),
304 Value::Con(ba_id, ba_fields)
306 if ba_fields.len() == 1
307 && table.get_by_name("ByteArray") == Some(*ba_id) =>
308 {
309 match &ba_fields[0] {
310 Value::ByteArray(bs) => bs
311 .lock()
312 .map_err(|_| BridgeError::InternalError("mutex poisoned".into()))?
313 .clone(),
314 _ => {
315 return Err(type_mismatch("ByteArray# in ByteArray", &ba_fields[0]))
316 }
317 }
318 }
319 _ => return Err(type_mismatch("ByteArray or ByteArray# in Text", &fields[0])),
320 };
321 let off = i64::from_value(&fields[1], table)? as usize;
322 let len = i64::from_value(&fields[2], table)? as usize;
323 if off + len > ba.len() {
324 return Err(BridgeError::TypeMismatch {
325 expected: "valid Text slice".to_string(),
326 got: format!("off={}, len={}, ba_len={}", off, len, ba.len()),
327 });
328 }
329 String::from_utf8(ba[off..off + len].to_vec()).map_err(|e| {
330 BridgeError::TypeMismatch {
331 expected: "UTF-8 Text".to_string(),
332 got: format!("Invalid UTF-8: {}", e),
333 }
334 })
335 }
336 Value::Lit(Literal::LitString(bytes)) => {
337 String::from_utf8(bytes.clone()).map_err(|e| BridgeError::TypeMismatch {
338 expected: "UTF-8 String".to_string(),
339 got: format!("Invalid UTF-8: {}", e),
340 })
341 }
342 Value::Con(_, _) => {
344 let mut chars = Vec::new();
345 let mut cur = value;
346 loop {
347 match cur {
348 Value::Con(tag, fields)
349 if table.get_by_name("[]") == Some(*tag) && fields.is_empty() =>
350 {
351 break;
352 }
353 Value::Con(tag, fields)
354 if table.get_by_name(":") == Some(*tag) && fields.len() == 2 =>
355 {
356 match &fields[0] {
357 Value::Lit(Literal::LitChar(c)) => chars.push(*c),
358 Value::Con(box_tag, box_fields)
360 if table.get_by_name("C#") == Some(*box_tag)
361 && box_fields.len() == 1 =>
362 {
363 match &box_fields[0] {
364 Value::Lit(Literal::LitChar(c)) => chars.push(*c),
365 other => return Err(type_mismatch("Char in C#", other)),
366 }
367 }
368 other => return Err(type_mismatch("Char or C#", other)),
369 }
370 cur = &fields[1];
371 }
372 _ => return Err(type_mismatch("[] or (:)", cur)),
373 }
374 }
375 Ok(chars.into_iter().collect())
376 }
377 _ => Err(type_mismatch("Text, LitString, or [Char]", value)),
378 }
379 }
380}
381
382impl ToCore for String {
383 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
384 let text_id = table
385 .get_by_name("Text")
386 .ok_or_else(|| BridgeError::UnknownDataConName("Text".into()))?;
387 let bytes = self.as_bytes().to_vec();
388 let len = bytes.len() as i64;
389 let ba_raw = Value::ByteArray(Arc::new(Mutex::new(bytes)));
395 Ok(Value::Con(
396 text_id,
397 vec![
398 ba_raw,
399 Value::Lit(Literal::LitInt(0)),
400 Value::Lit(Literal::LitInt(len)),
401 ],
402 ))
403 }
404}
405
406impl<T: FromCore> FromCore for Option<T> {
409 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
410 match value {
411 Value::Con(id, fields) => {
412 let nothing_id = table
413 .get_by_name("Nothing")
414 .ok_or(BridgeError::UnknownDataConName("Nothing".into()))?;
415 let just_id = table
416 .get_by_name("Just")
417 .ok_or(BridgeError::UnknownDataConName("Just".into()))?;
418
419 if *id == nothing_id {
420 if fields.is_empty() {
421 Ok(None)
422 } else {
423 Err(BridgeError::ArityMismatch {
424 con: *id,
425 expected: 0,
426 got: fields.len(),
427 })
428 }
429 } else if *id == just_id {
430 if fields.len() == 1 {
431 Ok(Some(T::from_value(&fields[0], table)?))
432 } else {
433 Err(BridgeError::ArityMismatch {
434 con: *id,
435 expected: 1,
436 got: fields.len(),
437 })
438 }
439 } else {
440 Err(BridgeError::UnknownDataCon(*id))
441 }
442 }
443 _ => Err(type_mismatch("Con", value)),
444 }
445 }
446}
447
448impl<T: ToCore> ToCore for Option<T> {
449 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
450 match self {
451 None => {
452 let id = table
453 .get_by_name("Nothing")
454 .ok_or_else(|| BridgeError::UnknownDataConName("Nothing".into()))?;
455 Ok(Value::Con(id, vec![]))
456 }
457 Some(x) => {
458 let id = table
459 .get_by_name("Just")
460 .ok_or_else(|| BridgeError::UnknownDataConName("Just".into()))?;
461 Ok(Value::Con(id, vec![x.to_value(table)?]))
462 }
463 }
464 }
465}
466
467impl<T: FromCore> FromCore for Vec<T> {
468 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
469 let nil_id = table
470 .get_by_name("[]")
471 .ok_or(BridgeError::UnknownDataConName("[]".into()))?;
472 let cons_id = table
473 .get_by_name(":")
474 .ok_or(BridgeError::UnknownDataConName(":".into()))?;
475
476 let mut res = Vec::new();
477 let mut curr = value;
478
479 loop {
480 match curr {
481 Value::Con(id, fields) => {
482 if *id == nil_id {
483 if fields.is_empty() {
484 break;
485 } else {
486 return Err(BridgeError::ArityMismatch {
487 con: *id,
488 expected: 0,
489 got: fields.len(),
490 });
491 }
492 } else if *id == cons_id {
493 if fields.len() == 2 {
494 res.push(T::from_value(&fields[0], table)?);
495 curr = &fields[1];
496 } else {
497 return Err(BridgeError::ArityMismatch {
498 con: *id,
499 expected: 2,
500 got: fields.len(),
501 });
502 }
503 } else {
504 return Err(BridgeError::UnknownDataCon(*id));
505 }
506 }
507 _ => return Err(type_mismatch("Con", curr)),
508 }
509 }
510
511 Ok(res)
512 }
513}
514
515impl<T: ToCore> ToCore for Vec<T> {
516 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
517 let nil_id = table
518 .get_by_name("[]")
519 .ok_or_else(|| BridgeError::UnknownDataConName("[]".into()))?;
520 let cons_id = table
521 .get_by_name(":")
522 .ok_or_else(|| BridgeError::UnknownDataConName(":".into()))?;
523
524 let mut res = Value::Con(nil_id, vec![]);
525 for x in self.iter().rev() {
526 res = Value::Con(cons_id, vec![x.to_value(table)?, res]);
527 }
528 Ok(res)
529 }
530}
531
532impl<T: FromCore, E: FromCore> FromCore for Result<T, E> {
533 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
534 match value {
535 Value::Con(id, fields) => {
536 let right_id = table
537 .get_by_name("Right")
538 .or_else(|| table.get_by_name("Ok"))
539 .ok_or(BridgeError::UnknownDataConName("Right/Ok".into()))?;
540 let left_id = table
541 .get_by_name("Left")
542 .or_else(|| table.get_by_name("Err"))
543 .ok_or(BridgeError::UnknownDataConName("Left/Err".into()))?;
544
545 if *id == right_id {
546 if fields.len() == 1 {
547 Ok(Ok(T::from_value(&fields[0], table)?))
548 } else {
549 Err(BridgeError::ArityMismatch {
550 con: *id,
551 expected: 1,
552 got: fields.len(),
553 })
554 }
555 } else if *id == left_id {
556 if fields.len() == 1 {
557 Ok(Err(E::from_value(&fields[0], table)?))
558 } else {
559 Err(BridgeError::ArityMismatch {
560 con: *id,
561 expected: 1,
562 got: fields.len(),
563 })
564 }
565 } else {
566 Err(BridgeError::UnknownDataCon(*id))
567 }
568 }
569 _ => Err(type_mismatch("Con", value)),
570 }
571 }
572}
573
574impl<T: ToCore, E: ToCore> ToCore for Result<T, E> {
575 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
576 match self {
577 Ok(x) => {
578 let id = table
579 .get_by_name("Right")
580 .or_else(|| table.get_by_name("Ok"))
581 .ok_or_else(|| BridgeError::UnknownDataConName("Right/Ok".into()))?;
582 Ok(Value::Con(id, vec![x.to_value(table)?]))
583 }
584 Err(e) => {
585 let id = table
586 .get_by_name("Left")
587 .or_else(|| table.get_by_name("Err"))
588 .ok_or_else(|| BridgeError::UnknownDataConName("Left/Err".into()))?;
589 Ok(Value::Con(id, vec![e.to_value(table)?]))
590 }
591 }
592 }
593}
594
595impl<A: FromCore, B: FromCore> FromCore for (A, B) {
598 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
599 match value {
600 Value::Con(id, fields) => {
601 let pair_id = table
602 .get_by_name("(,)")
603 .ok_or(BridgeError::UnknownDataConName("(,)".into()))?;
604 if *id == pair_id {
605 if fields.len() == 2 {
606 Ok((
607 A::from_value(&fields[0], table)?,
608 B::from_value(&fields[1], table)?,
609 ))
610 } else {
611 Err(BridgeError::ArityMismatch {
612 con: *id,
613 expected: 2,
614 got: fields.len(),
615 })
616 }
617 } else {
618 Err(BridgeError::UnknownDataCon(*id))
619 }
620 }
621 _ => Err(type_mismatch("Con", value)),
622 }
623 }
624}
625
626impl<A: ToCore, B: ToCore> ToCore for (A, B) {
627 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
628 let pair_id = table
629 .get_by_name("(,)")
630 .ok_or_else(|| BridgeError::UnknownDataConName("(,)".into()))?;
631 Ok(Value::Con(
632 pair_id,
633 vec![self.0.to_value(table)?, self.1.to_value(table)?],
634 ))
635 }
636}
637
638impl<A: FromCore, B: FromCore, C: FromCore> FromCore for (A, B, C) {
639 fn from_value(value: &Value, table: &DataConTable) -> Result<Self, BridgeError> {
640 match value {
641 Value::Con(id, fields) => {
642 let triple_id = table
643 .get_by_name("(,,)")
644 .ok_or(BridgeError::UnknownDataConName("(,,)".into()))?;
645 if *id == triple_id {
646 if fields.len() == 3 {
647 Ok((
648 A::from_value(&fields[0], table)?,
649 B::from_value(&fields[1], table)?,
650 C::from_value(&fields[2], table)?,
651 ))
652 } else {
653 Err(BridgeError::ArityMismatch {
654 con: *id,
655 expected: 3,
656 got: fields.len(),
657 })
658 }
659 } else {
660 Err(BridgeError::UnknownDataCon(*id))
661 }
662 }
663 _ => Err(type_mismatch("Con", value)),
664 }
665 }
666}
667
668impl<A: ToCore, B: ToCore, C: ToCore> ToCore for (A, B, C) {
669 fn to_value(&self, table: &DataConTable) -> Result<Value, BridgeError> {
670 let triple_id = table
671 .get_by_name("(,,)")
672 .ok_or_else(|| BridgeError::UnknownDataConName("(,,)".into()))?;
673 Ok(Value::Con(
674 triple_id,
675 vec![
676 self.0.to_value(table)?,
677 self.1.to_value(table)?,
678 self.2.to_value(table)?,
679 ],
680 ))
681 }
682}
683
684#[cfg(test)]
685mod tests {
686 use super::*;
687 use tidepool_repr::{DataCon, DataConId};
688
689 fn test_table() -> DataConTable {
690 let mut t = DataConTable::new();
691 t.insert(DataCon {
694 id: DataConId(0),
695 name: "Nothing".into(),
696 tag: 1,
697 rep_arity: 0,
698 field_bangs: vec![],
699 qualified_name: None,
700 });
701 t.insert(DataCon {
702 id: DataConId(1),
703 name: "Just".into(),
704 tag: 2,
705 rep_arity: 1,
706 field_bangs: vec![],
707 qualified_name: None,
708 });
709 t.insert(DataCon {
710 id: DataConId(2),
711 name: "False".into(),
712 tag: 1,
713 rep_arity: 0,
714 field_bangs: vec![],
715 qualified_name: None,
716 });
717 t.insert(DataCon {
718 id: DataConId(3),
719 name: "True".into(),
720 tag: 2,
721 rep_arity: 0,
722 field_bangs: vec![],
723 qualified_name: None,
724 });
725 t.insert(DataCon {
726 id: DataConId(4),
727 name: "(,)".into(),
728 tag: 1,
729 rep_arity: 2,
730 field_bangs: vec![],
731 qualified_name: None,
732 });
733 t.insert(DataCon {
734 id: DataConId(5),
735 name: "[]".into(),
736 tag: 1,
737 rep_arity: 0,
738 field_bangs: vec![],
739 qualified_name: None,
740 });
741 t.insert(DataCon {
742 id: DataConId(6),
743 name: ":".into(),
744 tag: 2,
745 rep_arity: 2,
746 field_bangs: vec![],
747 qualified_name: None,
748 });
749 t.insert(DataCon {
750 id: DataConId(7),
751 name: "(,,)".into(),
752 tag: 1,
753 rep_arity: 3,
754 field_bangs: vec![],
755 qualified_name: None,
756 });
757 t.insert(DataCon {
758 id: DataConId(8),
759 name: "Right".into(),
760 tag: 2,
761 rep_arity: 1,
762 field_bangs: vec![],
763 qualified_name: None,
764 });
765 t.insert(DataCon {
766 id: DataConId(9),
767 name: "Left".into(),
768 tag: 1,
769 rep_arity: 1,
770 field_bangs: vec![],
771 qualified_name: None,
772 });
773 t.insert(DataCon {
774 id: DataConId(10),
775 name: "I#".into(),
776 tag: 1,
777 rep_arity: 1,
778 field_bangs: vec![],
779 qualified_name: None,
780 });
781 t.insert(DataCon {
782 id: DataConId(11),
783 name: "W#".into(),
784 tag: 1,
785 rep_arity: 1,
786 field_bangs: vec![],
787 qualified_name: None,
788 });
789 t.insert(DataCon {
790 id: DataConId(12),
791 name: "D#".into(),
792 tag: 1,
793 rep_arity: 1,
794 field_bangs: vec![],
795 qualified_name: None,
796 });
797 t.insert(DataCon {
798 id: DataConId(13),
799 name: "C#".into(),
800 tag: 1,
801 rep_arity: 1,
802 field_bangs: vec![],
803 qualified_name: None,
804 });
805 t.insert(DataCon {
806 id: DataConId(14),
807 name: "()".into(),
808 tag: 1,
809 rep_arity: 0,
810 field_bangs: vec![],
811 qualified_name: None,
812 });
813 t.insert(DataCon {
814 id: DataConId(15),
815 name: "Text".into(),
816 tag: 1,
817 rep_arity: 3,
818 field_bangs: vec![],
819 qualified_name: None,
820 });
821 t
822 }
823
824 fn roundtrip<T: FromCore + ToCore + PartialEq + std::fmt::Debug>(val: T, table: &DataConTable) {
825 let value = val.to_value(table).expect("ToValue failed");
826 let back = T::from_value(&value, table).expect("FromValue failed");
827 assert_eq!(val, back);
828 }
829
830 #[test]
831 fn test_i64_roundtrip() {
832 let table = test_table();
833 roundtrip(42i64, &table);
834 roundtrip(-7i64, &table);
835 }
836
837 #[test]
838 fn test_i32_roundtrip() {
839 let table = test_table();
840 roundtrip(42i32, &table);
841 roundtrip(-7i32, &table);
842 }
843
844 #[test]
845 fn test_i32_overflow() {
846 let table = test_table();
847 let val: i64 = i32::MAX as i64 + 1;
848 let value = val.to_value(&table).unwrap();
849 let res = i32::from_value(&value, &table);
850 assert!(matches!(res, Err(BridgeError::TypeMismatch { .. })));
851 }
852
853 #[test]
854 fn test_u64_roundtrip() {
855 let table = test_table();
856 roundtrip(42u64, &table);
857 }
858
859 #[test]
860 fn test_f64_roundtrip() {
861 let table = test_table();
862 roundtrip(3.14159f64, &table);
863 roundtrip(-0.0f64, &table);
864 }
865
866 #[test]
867 fn test_bool_roundtrip() {
868 let table = test_table();
869 roundtrip(true, &table);
870 roundtrip(false, &table);
871 }
872
873 #[test]
874 fn test_char_roundtrip() {
875 let table = test_table();
876 roundtrip('a', &table);
877 roundtrip('λ', &table);
878 }
879
880 #[test]
881 fn test_string_roundtrip() {
882 let table = test_table();
883 roundtrip("hello".to_string(), &table);
884 roundtrip("".to_string(), &table);
885 }
886
887 #[test]
888 fn test_option_roundtrip() {
889 let table = test_table();
890 roundtrip(Some(42i64), &table);
891 roundtrip(None::<i64>, &table);
892 }
893
894 #[test]
895 fn test_vec_roundtrip() {
896 let table = test_table();
897 roundtrip(vec![1i64, 2, 3], &table);
898 roundtrip(Vec::<i64>::new(), &table);
899 }
900
901 #[test]
902 fn test_result_roundtrip() {
903 let table = test_table();
904 roundtrip(Ok::<i64, String>(42), &table);
905 roundtrip(Err::<i64, String>("error".to_string()), &table);
906 }
907
908 #[test]
909 fn test_tuple2_roundtrip() {
910 let table = test_table();
911 roundtrip((42i64, true), &table);
912 }
913
914 #[test]
915 fn test_tuple3_roundtrip() {
916 let table = test_table();
917 roundtrip((42i64, true, "hello".to_string()), &table);
918 }
919
920 #[test]
921 fn test_nested_roundtrip() {
922 let table = test_table();
923 roundtrip(vec![Some(1i64), None, Some(3)], &table);
924 roundtrip(Some((42i64, vec![true, false])), &table);
925 }
926
927 #[test]
928 fn test_unknown_datacon() {
929 let table = test_table();
930 let value = Value::Con(DataConId(100), vec![]);
931 let res = bool::from_value(&value, &table);
932 assert!(matches!(
933 res,
934 Err(BridgeError::UnknownDataCon(DataConId(100)))
935 ));
936 }
937
938 #[test]
939 fn test_arity_mismatch() {
940 let table = test_table();
941 let true_id = table.get_by_name("True").unwrap();
942 let value = Value::Con(true_id, vec![Value::Lit(Literal::LitInt(1))]);
943 let res = bool::from_value(&value, &table);
944 assert!(matches!(res, Err(BridgeError::ArityMismatch { .. })));
945 }
946
947 #[test]
948 fn test_type_mismatch() {
949 let table = test_table();
950 let value = Value::Lit(Literal::LitInt(1));
951 let res = bool::from_value(&value, &table);
952 assert!(matches!(res, Err(BridgeError::TypeMismatch { .. })));
953 }
954
955 #[test]
956 fn test_string_unboxed_fields() {
957 let table = test_table();
958 let s = "hello".to_string();
959 let value = s.to_value(&table).expect("ToValue failed");
960
961 if let Value::Con(id, fields) = &value {
962 assert_eq!(table.name_of(*id), Some("Text"));
963 assert_eq!(fields.len(), 3);
964 assert!(matches!(fields[0], Value::ByteArray(_)));
966 assert!(matches!(fields[1], Value::Lit(Literal::LitInt(0))));
968 assert!(matches!(fields[2], Value::Lit(Literal::LitInt(5))));
970 } else {
971 panic!("Expected Con, got {:?}", value);
972 }
973 }
974
975 #[test]
976 fn test_unit_roundtrip() {
977 let table = test_table();
978 roundtrip((), &table);
979 }
980
981 #[test]
982 fn test_f64_boxed_roundtrip() {
983 let table = test_table();
984 roundtrip(3.14159f64, &table);
985 }
986
987 #[test]
988 fn test_u64_boxed_roundtrip() {
989 let table = test_table();
990 roundtrip(42u64, &table);
991 }
992
993 #[test]
994 fn test_char_boxed_roundtrip() {
995 let table = test_table();
996 roundtrip('a', &table);
997 }
998
999 #[test]
1000 fn test_vec_string_roundtrip() {
1001 let table = test_table();
1002 roundtrip(vec!["a".to_string(), "b".to_string()], &table);
1003 }
1004
1005 #[test]
1006 fn test_option_nested_roundtrip() {
1007 let table = test_table();
1008 roundtrip(Some(vec![1i64, 2]), &table);
1009 roundtrip(None::<Vec<i64>>, &table);
1010 }
1011
1012 #[test]
1013 fn test_result_nested_roundtrip() {
1014 let table = test_table();
1015 roundtrip(Ok::<Vec<i64>, String>(vec![1, 2]), &table);
1016 roundtrip(Err::<Vec<i64>, String>("error".to_string()), &table);
1017 }
1018}