#![feature(try_from)] use std::collections::BTreeMap; use std::fmt::Debug; use std::convert::TryFrom; #[derive(Debug, Clone, PartialEq)] pub enum Value { Double(f64), Text(String), } #[derive(Debug)] pub enum DaoError<'a, T> where T: TryFrom<&'a Value>, >::Error: Debug, { UglyConvertError(>::Error), // this is ugly ConvertError(ConvertError), // why can't it be wrapped into this? NoSuchValueError(String), } #[derive(Debug)] pub enum ConvertError { NotSupported(String), } #[derive(Debug, PartialEq)] pub struct Dao<'a>(BTreeMap<&'a str, Value>); impl<'a> Dao<'a> { pub fn new() -> Self { Dao(BTreeMap::new()) } pub fn insert(&mut self, s: &'a str, v: V) where V: Into, { self.0.insert(s, v.into()); } pub fn get(&'a self, s: &str) -> Result> where T: TryFrom<&'a Value>, >::Error: Debug, { let value: Option<&'a Value> = self.0.get(s); match value { Some(v) => TryFrom::try_from(v).map_err(|e| DaoError::UglyConvertError(e)), None => Err(DaoError::NoSuchValueError(s.into())), } } /* //I wanted a simpler Error signature, but this is erroneous pub fn simpl_get(&'a self, s: &str) -> Result> where T: TryFrom<&'a Value> { let value: Option<&'a Value> = self.0.get(s); match value { Some(v) => TryFrom::try_from(v).map_err(|e| DaoError::ConvertError(e)), None => Err(DaoError::NoSuchValueError(s.into())), } } */ } impl<'a> TryFrom<&'a Value> for f64 { type Error = ConvertError; fn try_from(value: &'a Value) -> Result { match *value { Value::Double(v) => Ok(v), _ => Err(ConvertError::NotSupported("can not convert to f64".into())), } } } impl<'a> TryFrom<&'a Value> for String { type Error = ConvertError; fn try_from(value: &'a Value) -> Result { match *value { Value::Text(ref v) => Ok(v.to_owned()), _ => Err(ConvertError::NotSupported( "can not convert to String".into(), )), } } } impl From for Value { fn from(f: f64) -> Self { Value::Double(f) } } impl<'a> From<&'a str> for Value { fn from(f: &str) -> Self { Value::Text(f.to_owned()) } } fn main() { let mut dao = Dao::new(); dao.insert("life", 42.0f64); dao.insert("lemons", "lemonade"); let life: Result = dao.get("life"); println!("life: {:#?}", life); assert!(life.is_ok()); if let Ok(life) = life { assert_eq!(life, 42.0f64); }; let lemons: Result = dao.get("lemons"); println!("lemons?: {:#?}", lemons); assert!(lemons.is_ok()); if let Ok(lemons) = lemons { assert_eq!(lemons, "lemonade"); } }