use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::Arc;
use crate::kurbo::{Point, Rect, Size};
use crate::piet::{Color, LinearGradient};
use crate::localization::L10nManager;
use crate::Data;
#[derive(Clone)]
pub struct Env(Arc<EnvImpl>);
#[derive(Clone)]
struct EnvImpl {
map: HashMap<String, Value>,
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),
LinearGradient(Arc<LinearGradient>),
Float(f64),
String(String),
}
pub trait ValueType<'a>: Sized {
type Owned: Into<Value>;
fn try_from_value(v: &'a Value) -> Result<Self, EnvError>;
}
pub type EnvError = String;
impl Env {
pub fn get<'a, V: ValueType<'a>>(&'a self, key: Key<V>) -> V {
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: Key<V>) -> Option<V> {
self.0
.map
.get(key.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 fn localization_manager(&self) -> &L10nManager {
&self.0.l10n
}
}
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::LinearGradient(g) => write!(f, "LinearGradient {:?}", g),
Value::Float(x) => write!(f, "Float {}", x),
Value::String(s) => write!(f, "String {:?}", s),
}
}
}
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,
(LinearGradient(_), LinearGradient(_)) => true,
(Float(_), Float(_)) => true,
(String(_), String(_)) => true,
_ => false,
}
}
}
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(),
(LinearGradient(g1), LinearGradient(g2)) => Arc::ptr_eq(g1, g2),
(Float(f1), Float(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 std::default::Default for Env {
fn default() -> Self {
let l10n = L10nManager::new(vec!["builtin.ftl".into()], "./resources/i18n/");
let inner = EnvImpl {
l10n: Arc::new(l10n),
map: HashMap::new(),
};
Env(Arc::new(inner))
}
}
impl<T> From<Key<T>> for String {
fn from(src: Key<T>) -> String {
String::from(src.key)
}
}
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, EnvError> {
match value {
Value::$var(f) => Ok(f.to_owned()),
other => Err(format!(
"incorrect Value type. Expected {}, found {:?}",
stringify!($var),
other
)),
}
}
}
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, EnvError> {
match value {
Value::$var(f) => Ok(f),
other => Err(format!(
"incorrect Value type. Expected {}, found {:?}",
stringify!($var),
other
)),
}
}
}
impl Into<Value> for $owned {
fn into(self) -> Value {
Value::$var(self)
}
}
};
}
macro_rules! impl_value_type_arc {
($ty:ty, $var:ident) => {
impl<'a> ValueType<'a> for &'a $ty {
type Owned = $ty;
fn try_from_value(value: &'a Value) -> Result<Self, EnvError> {
match value {
Value::$var(f) => Ok(f),
other => Err(format!(
"incorrect Value type. Expected {}, found {:?}",
stringify!($var),
other
)),
}
}
}
impl Into<Value> for $ty {
fn into(self) -> Value {
Value::$var(Arc::new(self))
}
}
impl Into<Value> for Arc<$ty> {
fn into(self) -> Value {
Value::$var(self)
}
}
};
}
impl_value_type_owned!(f64, Float);
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_value_type_arc!(LinearGradient, LinearGradient);