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 100 101
use std::cell::{Ref, RefCell, RefMut}; use std::collections::HashMap; use std::fmt; use std::hash::{Hash, Hasher}; use std::rc::Rc; use crate::eval::Env; use crate::objects::{Datum, Eval, Object, Vtable}; use crate::unwind::Unwind; pub struct Record { data: RefCell<HashMap<String, Object>>, } impl Record { pub fn borrow(&self) -> Ref<HashMap<String, Object>> { self.data.borrow() } pub fn borrow_mut(&self) -> RefMut<HashMap<String, Object>> { self.data.borrow_mut() } } impl PartialEq for Record { fn eq(&self, other: &Self) -> bool { std::ptr::eq(self, other) } } impl Eq for Record {} impl Hash for Record { fn hash<H: Hasher>(&self, state: &mut H) { std::ptr::hash(self, state); } } impl fmt::Debug for Record { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{{")?; let mut first = true; for (k, v) in self.borrow().iter() { if first { first = false; } else { write!(f, ", ")?; } write!(f, "{}: {:?}", k, v)?; } write!(f, "}}") } } pub fn class_vtable() -> Vtable { let vt = Vtable::new("class Record"); vt.add_primitive_method_or_panic("perform:with:", class_record_perform_with); vt } pub fn instance_vtable() -> Vtable { let vt = Vtable::new("Record"); vt.add_primitive_method_or_panic("perform:with:", record_perform_with); vt.add_primitive_method_or_panic("displayOn:", record_display_on); vt } pub fn as_record<'a>(obj: &'a Object, ctx: &str) -> Result<&'a Record, Unwind> { match &obj.datum { Datum::Record(ref record) => Ok(record), _ => Unwind::error(&format!("{:?} is not a Record in {}", obj, ctx)), } } fn class_record_perform_with(_receiver: &Object, args: &[Object], env: &Env) -> Eval { let mut data = HashMap::new(); let selector = args[0].string_as_str(); let values = args[1].as_array("in Record##perform:with:")?.borrow(); for (k, v) in selector.split(':').zip(&*values) { data.insert(k.to_string(), v.clone()); } Ok(Object { vtable: env.foo.record_vtable.clone(), datum: Datum::Record(Rc::new(Record { data: RefCell::new(data), })), }) } fn record_perform_with(receiver: &Object, args: &[Object], _env: &Env) -> Eval { let r: &Record = receiver.as_record("in Record#perform:with:")?; let data: Ref<HashMap<_, _>> = r.borrow(); let selector = args[0].string_as_str(); match data.get(selector) { Some(obj) => Ok(obj.clone()), None => Unwind::message_error(receiver, selector, &args[1..2]), } } fn record_display_on(receiver: &Object, args: &[Object], env: &Env) -> Eval { args[0].send("print:", &[receiver.send("toString", &[], env)?], env) }