use std::any;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::Arc;
use crate::localization::L10nManager;
use crate::{Color, Data, Point, Rect, Size};
#[derive(Clone)]
pub struct Env(Arc<EnvImpl>);
#[derive(Clone)]
struct EnvImpl {
map: HashMap<String, Value>,
debug_colors: Vec<Color>,
l10n: Arc<L10nManager>,
}
pub struct Key<T> {
key: &'static str,
value_type: PhantomData<T>,
}
#[derive(Clone)]
pub enum Value {
Point(Point),
Size(Size),
Rect(Rect),
Color(Color),
Float(f64),
Bool(bool),
UnsignedInt(u64),
String(String),
}
pub enum KeyOrValue<T> {
Concrete(Value),
Key(Key<T>),
}
pub trait ValueType<'a>: Sized {
type Owned: Into<Value>;
fn try_from_value(v: &'a Value) -> Result<Self, ValueTypeError>;
}
#[derive(Debug, Clone)]
pub struct ValueTypeError {
expected: &'static str,
found: Value,
}
impl Env {
pub(crate) const DEBUG_PAINT: Key<bool> = Key::new("druid.built-in.debug-paint");
pub const DEBUG_WIDGET: Key<bool> = Key::new("druid.built-in.debug-widget");
pub fn get<'a, V: ValueType<'a>>(&'a self, key: impl Borrow<Key<V>>) -> V {
let key = key.borrow();
if let Some(value) = self.0.map.get(key.key) {
value.to_inner_unchecked()
} else {
panic!("key for {} not found", key.key)
}
}
pub fn try_get<'a, V: ValueType<'a>>(&'a self, key: impl Borrow<Key<V>>) -> Option<V> {
self.0
.map
.get(key.borrow().key)
.map(|value| value.to_inner_unchecked())
}
pub fn adding<'a, V: ValueType<'a>>(mut self, key: Key<V>, value: impl Into<V::Owned>) -> Env {
let env = Arc::make_mut(&mut self.0);
env.map.insert(key.into(), value.into().into());
self
}
pub fn set<'a, V: ValueType<'a>>(&'a mut self, key: Key<V>, value: impl Into<V::Owned>) {
let env = Arc::make_mut(&mut self.0);
let value = value.into().into();
let key = key.into();
if let Some(existing) = env.map.get(&key) {
if !existing.is_same_type(&value) {
panic!(
"Invalid type for key '{}': {:?} differs in kind from {:?}",
key, existing, value
);
}
}
env.map.insert(key, value);
}
pub(crate) fn localization_manager(&self) -> &L10nManager {
&self.0.l10n
}
#[doc(hidden)]
pub fn get_debug_color(&self, id: u64) -> Color {
let color_num = id as usize % self.0.debug_colors.len();
self.0.debug_colors[color_num].clone()
}
}
impl<T> Key<T> {
pub const fn new(key: &'static str) -> Self {
Key {
key,
value_type: PhantomData,
}
}
}
impl Value {
pub fn to_inner_unchecked<'a, V: ValueType<'a>>(&'a self) -> V {
match ValueType::try_from_value(self) {
Ok(v) => v,
Err(s) => panic!("{}", s),
}
}
fn is_same_type(&self, other: &Value) -> bool {
use Value::*;
match (self, other) {
(Point(_), Point(_)) => true,
(Size(_), Size(_)) => true,
(Rect(_), Rect(_)) => true,
(Color(_), Color(_)) => true,
(Float(_), Float(_)) => true,
(Bool(_), Bool(_)) => true,
(UnsignedInt(_), UnsignedInt(_)) => true,
(String(_), String(_)) => true,
_ => false,
}
}
}
impl Debug for Value {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
Value::Point(p) => write!(f, "Point {:?}", p),
Value::Size(s) => write!(f, "Size {:?}", s),
Value::Rect(r) => write!(f, "Rect {:?}", r),
Value::Color(c) => write!(f, "Color {:?}", c),
Value::Float(x) => write!(f, "Float {}", x),
Value::Bool(b) => write!(f, "Bool {}", b),
Value::UnsignedInt(x) => write!(f, "UnsignedInt {}", x),
Value::String(s) => write!(f, "String {:?}", s),
}
}
}
impl Data for Value {
fn same(&self, other: &Value) -> bool {
use Value::*;
match (self, other) {
(Point(p1), Point(p2)) => p1.x.same(&p2.x) && p1.y.same(&p2.y),
(Rect(r1), Rect(r2)) => {
r1.x0.same(&r2.x0) && r1.y0.same(&r2.y0) && r1.x1.same(&r2.x1) && r1.y1.same(&r2.y1)
}
(Size(s1), Size(s2)) => s1.width.same(&s2.width) && s1.height.same(&s2.height),
(Color(c1), Color(c2)) => c1.as_rgba_u32() == c2.as_rgba_u32(),
(Float(f1), Float(f2)) => f1.same(&f2),
(Bool(b1), Bool(b2)) => b1 == b2,
(UnsignedInt(f1), UnsignedInt(f2)) => f1.same(&f2),
(String(s1), String(s2)) => s1 == s2,
_ => false,
}
}
}
impl Data for Env {
fn same(&self, other: &Env) -> bool {
Arc::ptr_eq(&self.0, &other.0) || self.0.deref().same(other.0.deref())
}
}
impl Data for EnvImpl {
fn same(&self, other: &EnvImpl) -> bool {
self.map.len() == other.map.len()
&& self
.map
.iter()
.all(|(k, v1)| other.map.get(k).map(|v2| v1.same(v2)).unwrap_or(false))
}
}
impl Default for Env {
fn default() -> Self {
let l10n = L10nManager::new(vec!["builtin.ftl".into()], "./resources/i18n/");
let debug_colors = vec![
Color::rgb8(230, 25, 75),
Color::rgb8(60, 180, 75),
Color::rgb8(255, 225, 25),
Color::rgb8(0, 130, 200),
Color::rgb8(245, 130, 48),
Color::rgb8(70, 240, 240),
Color::rgb8(240, 50, 230),
Color::rgb8(250, 190, 190),
Color::rgb8(0, 128, 128),
Color::rgb8(230, 190, 255),
Color::rgb8(170, 110, 40),
Color::rgb8(255, 250, 200),
Color::rgb8(128, 0, 0),
Color::rgb8(170, 255, 195),
Color::rgb8(0, 0, 128),
Color::rgb8(128, 128, 128),
Color::rgb8(255, 255, 255),
Color::rgb8(0, 0, 0),
];
let inner = EnvImpl {
l10n: Arc::new(l10n),
map: HashMap::new(),
debug_colors,
};
Env(Arc::new(inner))
.adding(Env::DEBUG_PAINT, false)
.adding(Env::DEBUG_WIDGET, false)
}
}
impl<T> From<Key<T>> for String {
fn from(src: Key<T>) -> String {
String::from(src.key)
}
}
impl ValueTypeError {
fn new(expected: &'static str, found: Value) -> ValueTypeError {
ValueTypeError { expected, found }
}
}
impl std::fmt::Display for ValueTypeError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"Incorrect value type: expected {} found {:?}",
self.expected, self.found
)
}
}
impl std::error::Error for ValueTypeError {}
macro_rules! impl_value_type_owned {
($ty:ty, $var:ident) => {
impl<'a> ValueType<'a> for $ty {
type Owned = $ty;
fn try_from_value(value: &Value) -> Result<Self, ValueTypeError> {
match value {
Value::$var(f) => Ok(f.to_owned()),
other => Err(ValueTypeError::new(any::type_name::<$ty>(), other.clone())),
}
}
}
impl Into<Value> for $ty {
fn into(self) -> Value {
Value::$var(self)
}
}
};
}
macro_rules! impl_value_type_borrowed {
($ty:ty, $owned:ty, $var:ident) => {
impl<'a> ValueType<'a> for &'a $ty {
type Owned = $owned;
fn try_from_value(value: &'a Value) -> Result<Self, ValueTypeError> {
match value {
Value::$var(f) => Ok(f),
other => Err(ValueTypeError::new(any::type_name::<$ty>(), other.clone())),
}
}
}
impl Into<Value> for $owned {
fn into(self) -> Value {
Value::$var(self)
}
}
};
}
impl_value_type_owned!(f64, Float);
impl_value_type_owned!(bool, Bool);
impl_value_type_owned!(u64, UnsignedInt);
impl_value_type_owned!(Color, Color);
impl_value_type_owned!(Rect, Rect);
impl_value_type_owned!(Point, Point);
impl_value_type_owned!(Size, Size);
impl_value_type_borrowed!(str, String, String);
impl<'a, T: ValueType<'a>> KeyOrValue<T> {
pub fn resolve(&'a self, env: &'a Env) -> T {
match self {
KeyOrValue::Concrete(value) => value.to_inner_unchecked(),
KeyOrValue::Key(key) => env.get(key),
}
}
}
impl<'a, V: Into<Value>, T: ValueType<'a, Owned = V>> From<V> for KeyOrValue<T> {
fn from(value: V) -> KeyOrValue<T> {
KeyOrValue::Concrete(value.into())
}
}
impl<'a, T: ValueType<'a>> From<Key<T>> for KeyOrValue<T> {
fn from(key: Key<T>) -> KeyOrValue<T> {
KeyOrValue::Key(key)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn string_key_or_value() {
const MY_KEY: Key<&str> = Key::new("test.my-string-key");
let env = Env::default().adding(MY_KEY, "Owned".to_string());
assert_eq!(env.get(MY_KEY), "Owned");
let key: KeyOrValue<&str> = MY_KEY.into();
let value: KeyOrValue<&str> = "Owned".to_string().into();
assert_eq!(key.resolve(&env), value.resolve(&env));
}
}