use std::{
any::{Any, TypeId},
fmt,
marker::PhantomData,
ops,
sync::{Arc, atomic::AtomicBool},
};
use crate::{
AnyVarHookArgs, AnyVarValue, BoxAnyVarValue, VarInstanceTag, VarUpdateId, VarValue,
animation::{AnimationStopFn, ModifyInfo},
read_only_var::ReadOnlyImpl,
};
use bitflags::bitflags;
use smallbox::{SmallBox, smallbox};
pub(crate) mod shared_var;
pub use shared_var::{any_var, any_var_derived, var, var_derived, var_getter, var_state};
pub(crate) mod const_var;
pub(crate) mod cow_var;
pub use const_var::IntoVar;
pub(crate) mod flat_map_var;
pub(crate) mod read_only_var;
pub(crate) mod contextual_var;
pub use contextual_var::{ContextInitHandle, WeakContextInitHandle, any_contextual_var, contextual_var};
pub(crate) mod context_var;
pub use context_var::{__context_var_local, ContextVar, context_var_init};
pub(crate) mod merge_var;
pub use merge_var::{
__merge_var, MergeInput, MergeVarBuilder, VarMergeInputs, merge_var, merge_var_input, merge_var_output, merge_var_with,
};
pub(crate) mod response_var;
pub use response_var::{ResponderVar, Response, ResponseVar, response_done_var, response_var};
pub(crate) mod when_var;
pub use when_var::{__when_var, AnyWhenVarBuilder, WhenVarBuilder};
pub(crate) mod expr_var;
pub use expr_var::{__expr_var, expr_var_as, expr_var_into, expr_var_map};
pub(crate) enum DynAnyVar {
Const(const_var::ConstVar),
Merge(merge_var::MergeVar),
When(when_var::WhenVar),
Shared(shared_var::SharedVar),
Context(context_var::ContextVarImpl),
FlatMap(flat_map_var::FlatMapVar),
Cow(cow_var::CowVar),
Contextual(contextual_var::ContextualVar),
ReadOnlyShared(ReadOnlyImpl<shared_var::SharedVar>),
ReadOnlyFlatMap(ReadOnlyImpl<flat_map_var::FlatMapVar>),
ReadOnlyContext(ReadOnlyImpl<context_var::ContextVarImpl>),
ReadOnlyCow(ReadOnlyImpl<cow_var::CowVar>),
ReadOnlyContextual(ReadOnlyImpl<contextual_var::ContextualVar>),
}
macro_rules! dispatch {
($self:ident, $var:ident => $($tt:tt)+) => {
match $self {
DynAnyVar::Const($var) => $($tt)+,
DynAnyVar::Merge($var) => $($tt)+,
DynAnyVar::FlatMap($var) => $($tt)+,
DynAnyVar::When($var) => $($tt)+,
DynAnyVar::Shared($var) => $($tt)+,
DynAnyVar::Context($var) => $($tt)+,
DynAnyVar::Cow($var) => $($tt)+,
DynAnyVar::Contextual($var) => $($tt)+,
DynAnyVar::ReadOnlyShared($var) => $($tt)+,
DynAnyVar::ReadOnlyFlatMap($var) => $($tt)+,
DynAnyVar::ReadOnlyContext($var) => $($tt)+,
DynAnyVar::ReadOnlyCow($var) => $($tt)+,
DynAnyVar::ReadOnlyContextual($var) => $($tt)+,
}
};
}
impl fmt::Debug for DynAnyVar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
dispatch!(self, v => fmt::Debug::fmt(v, f))
}
}
pub(crate) enum DynWeakAnyVar {
Const(const_var::WeakConstVar),
Merge(merge_var::WeakMergeVar),
When(when_var::WeakWhenVar),
Shared(shared_var::WeakSharedVar),
Context(context_var::ContextVarImpl),
FlatMap(flat_map_var::WeakFlatMapVar),
Cow(cow_var::WeakCowVar),
Contextual(contextual_var::WeakContextualVar),
ReadOnlyShared(ReadOnlyImpl<shared_var::WeakSharedVar>),
ReadOnlyContext(ReadOnlyImpl<context_var::ContextVarImpl>),
ReadOnlyCow(ReadOnlyImpl<cow_var::WeakCowVar>),
ReadOnlyContextual(ReadOnlyImpl<contextual_var::WeakContextualVar>),
ReadOnlyFlatMap(ReadOnlyImpl<flat_map_var::WeakFlatMapVar>),
}
macro_rules! dispatch_weak {
($self:ident, $var:ident => $($tt:tt)+) => {
match $self {
DynWeakAnyVar::Const($var) => $($tt)+,
DynWeakAnyVar::Shared($var) => $($tt)+,
DynWeakAnyVar::Context($var) => $($tt)+,
DynWeakAnyVar::Cow($var) => $($tt)+,
DynWeakAnyVar::Contextual($var) => $($tt)+,
DynWeakAnyVar::FlatMap($var) => $($tt)+,
DynWeakAnyVar::Merge($var) => $($tt)+,
DynWeakAnyVar::When($var) => $($tt)+,
DynWeakAnyVar::ReadOnlyShared($var) => $($tt)+,
DynWeakAnyVar::ReadOnlyContext($var) => $($tt)+,
DynWeakAnyVar::ReadOnlyCow($var) => $($tt)+,
DynWeakAnyVar::ReadOnlyContextual($var) => $($tt)+,
DynWeakAnyVar::ReadOnlyFlatMap($var) => $($tt)+,
}
};
}
impl fmt::Debug for DynWeakAnyVar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
dispatch_weak!(self, v => fmt::Debug::fmt(v, f))
}
}
macro_rules! declare {
($(
$(#[$meta:meta])*
fn $method:ident(&self $(, $arg:ident : $Input:ty)*) $(-> $Output:ty)?;
)+) => {
pub(crate) trait VarImpl: fmt::Debug + Any + Send + Sync {
$(
$(#[$meta])*
fn $method(&self $(, $arg: $Input)*) $(-> $Output)?;
)+
}
impl VarImpl for DynAnyVar {
$(
$(#[$meta])*
fn $method(&self $(, $arg: $Input)*) $(-> $Output)? {
dispatch!(self, v => VarImpl::$method(v$(, $arg)*))
}
)+
}
};
}
declare! {
fn clone_dyn(&self) -> DynAnyVar;
fn value_type(&self) -> TypeId;
#[cfg(feature = "type_names")]
fn value_type_name(&self) -> &'static str;
fn strong_count(&self) -> usize;
fn var_eq(&self, other: &DynAnyVar) -> bool;
fn var_instance_tag(&self) -> VarInstanceTag;
fn downgrade(&self) -> DynWeakAnyVar;
fn capabilities(&self) -> VarCapability;
fn with(&self, visitor: &mut dyn FnMut(&dyn AnyVarValue));
fn get(&self) -> BoxAnyVarValue;
fn set(&self, new_value: BoxAnyVarValue) -> bool;
fn update(&self) -> bool;
fn modify(&self, modify: SmallBox<dyn FnMut(&mut AnyVarModify) + Send + 'static, smallbox::space::S4>) -> bool;
fn hook(&self, on_new: SmallBox<dyn FnMut(&AnyVarHookArgs) -> bool + Send + 'static, smallbox::space::S4>) -> VarHandle;
fn last_update(&self) -> VarUpdateId;
fn modify_importance(&self) -> usize;
fn is_animating(&self) -> bool;
fn hook_animation_stop(&self, handler: AnimationStopFn) -> VarHandle;
fn current_context(&self) -> DynAnyVar;
fn modify_info(&self) -> ModifyInfo;
}
macro_rules! declare_weak {
($(
fn $method:ident(&self $(, $arg:ident : $Input:ty)*) $(-> $Output:ty)?;
)+) => {
pub(crate) trait WeakVarImpl: fmt::Debug + Any + Send + Sync {
$(
fn $method(&self $(, $arg: $Input)*) $(-> $Output)?;
)+
}
impl WeakVarImpl for DynWeakAnyVar {
$(
fn $method(&self $(, $arg: $Input)*) $(-> $Output)? {
dispatch_weak!(self, v => WeakVarImpl::$method(v$(, $arg)*))
}
)+
}
};
}
declare_weak! {
fn clone_dyn(&self) -> DynWeakAnyVar;
fn strong_count(&self) -> usize;
fn upgrade(&self) -> Option<DynAnyVar>;
fn var_eq(&self, other: &DynWeakAnyVar) -> bool;
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct VarIsReadOnlyError {}
impl fmt::Display for VarIsReadOnlyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "cannot modify read-only variable")
}
}
impl std::error::Error for VarIsReadOnlyError {}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VarCapability: u8 {
const NEW = 0b0000_0010;
const MODIFY = 0b0000_0011;
const CONTEXT = 0b1000_0000;
const MODIFY_CHANGES = 0b0100_0000;
const CONTEXT_CHANGES = 0b0010_0000;
const SHARE = 0b0001_0000;
}
}
impl VarCapability {
pub fn is_const(self) -> bool {
self.is_empty()
}
pub fn is_always_read_only(&self) -> bool {
!self.contains(Self::MODIFY) && !self.contains(Self::MODIFY_CHANGES)
}
pub fn is_read_only(self) -> bool {
!self.can_modify()
}
pub fn can_modify(self) -> bool {
self.contains(Self::MODIFY)
}
pub fn is_contextual(self) -> bool {
self.contains(Self::CONTEXT)
}
pub fn is_always_contextual(self) -> bool {
self.contains(Self::CONTEXT) && !self.contains(Self::CONTEXT_CHANGES)
}
pub fn is_share(&self) -> bool {
self.contains(Self::SHARE)
}
pub fn is_local(&self) -> bool {
!self.is_share()
}
}
impl VarCapability {
pub(crate) fn as_always_read_only(self) -> Self {
let mut out = self;
out.remove(Self::MODIFY & !Self::NEW);
out.remove(Self::MODIFY_CHANGES);
out
}
}
bitflags! {
#[derive(Clone, Copy)]
pub(crate) struct VarModifyUpdate: u8 {
const UPDATE = 0b001;
const REQUESTED = 0b011;
const TOUCHED = 0b101;
}
}
pub struct AnyVarModify<'a> {
pub(crate) value: &'a mut BoxAnyVarValue,
pub(crate) update: VarModifyUpdate,
pub(crate) tags: Vec<BoxAnyVarValue>,
pub(crate) custom_importance: Option<usize>,
}
impl<'a> AnyVarModify<'a> {
pub fn set(&mut self, mut new_value: BoxAnyVarValue) -> bool {
if **self.value != *new_value {
if !self.value.try_swap(&mut *new_value) {
#[cfg(feature = "type_names")]
panic!(
"cannot AnyVarModify::set `{}` on variable of type `{}`",
new_value.type_name(),
self.value.type_name()
);
#[cfg(not(feature = "type_names"))]
panic!("cannot modify set, type mismatch");
}
self.update |= VarModifyUpdate::TOUCHED;
true
} else {
false
}
}
pub fn update(&mut self) {
self.update |= VarModifyUpdate::REQUESTED;
}
pub fn tags(&self) -> &[BoxAnyVarValue] {
&self.tags
}
pub fn push_tag(&mut self, tag: impl AnyVarValue) {
self.tags.push(BoxAnyVarValue::new(tag));
}
pub fn set_modify_importance(&mut self, importance: usize) {
self.custom_importance = Some(importance);
}
pub fn downcast<'s, T: VarValue>(&'s mut self) -> Option<VarModify<'s, 'a, T>> {
if self.value.is::<T>() {
Some(VarModify {
inner: self,
_t: PhantomData,
})
} else {
None
}
}
pub fn value(&self) -> &dyn AnyVarValue {
&**self
}
pub fn value_mut(&mut self) -> &mut dyn AnyVarValue {
&mut **self
}
}
impl<'a> ops::Deref for AnyVarModify<'a> {
type Target = dyn AnyVarValue;
fn deref(&self) -> &Self::Target {
&**self.value
}
}
impl<'a> ops::DerefMut for AnyVarModify<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.update |= VarModifyUpdate::TOUCHED;
self.value.deref_mut()
}
}
pub struct VarModify<'s, 'a, T: VarValue> {
inner: &'s mut AnyVarModify<'a>,
_t: PhantomData<fn() -> &'a T>,
}
impl<'s, 'a, T: VarValue> VarModify<'s, 'a, T> {
pub fn set(&mut self, new_value: impl Into<T>) -> bool {
let new_value = new_value.into();
if **self != new_value {
**self = new_value;
true
} else {
false
}
}
pub fn update(&mut self) {
self.inner.update();
}
pub fn tags(&self) -> &[BoxAnyVarValue] {
self.inner.tags()
}
pub fn push_tag(&mut self, tag: impl AnyVarValue) {
self.inner.push_tag(tag);
}
pub fn set_modify_importance(&mut self, importance: usize) {
self.inner.set_modify_importance(importance);
}
pub fn as_any(&mut self) -> &mut AnyVarModify<'a> {
self.inner
}
pub fn value(&self) -> &T {
self
}
pub fn value_mut(&mut self) -> &mut T {
self
}
}
impl<'s, 'a, T: VarValue> ops::Deref for VarModify<'s, 'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.inner.downcast_ref().unwrap()
}
}
impl<'s, 'a, T: VarValue> ops::DerefMut for VarModify<'s, 'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.downcast_mut().unwrap()
}
}
#[derive(Clone, Default)]
#[must_use = "var handle stops the behavior it represents on drop"]
pub struct VarHandle(Option<Arc<AtomicBool>>);
impl PartialEq for VarHandle {
fn eq(&self, other: &Self) -> bool {
if let Some(a) = &self.0
&& let Some(b) = &other.0
{
Arc::ptr_eq(a, b)
} else {
false
}
}
}
impl fmt::Debug for VarHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_dummy() {
write!(f, "VarHandle(<dummy>)")
} else {
f.debug_tuple("VarHandle").finish_non_exhaustive()
}
}
}
impl VarHandle {
pub const fn dummy() -> Self {
VarHandle(None)
}
pub(crate) fn new() -> (VarHandlerOwner, Self) {
let h = Arc::new(AtomicBool::new(false));
(VarHandlerOwner(h.clone()), Self(Some(h)))
}
pub fn is_dummy(&self) -> bool {
self.0.is_none()
}
pub fn perm(self) {
if let Some(c) = &self.0 {
c.store(true, std::sync::atomic::Ordering::Relaxed);
}
}
pub fn chain(self, other: Self) -> VarHandles {
VarHandles(smallvec::smallvec![self, other])
}
pub fn downgrade(&self) -> WeakVarHandle {
match &self.0 {
Some(a) => WeakVarHandle(Arc::downgrade(a)),
None => WeakVarHandle::new(),
}
}
}
#[derive(Clone, Default)]
pub struct WeakVarHandle(std::sync::Weak<AtomicBool>);
impl PartialEq for WeakVarHandle {
fn eq(&self, other: &Self) -> bool {
self.0.ptr_eq(&other.0)
}
}
impl Eq for WeakVarHandle {}
impl fmt::Debug for WeakVarHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("WeakVarHandle").finish_non_exhaustive()
}
}
impl WeakVarHandle {
pub fn upgrade(&self) -> Option<VarHandle> {
let h = VarHandle(self.0.upgrade());
if h.is_dummy() { None } else { Some(h) }
}
pub const fn new() -> Self {
WeakVarHandle(std::sync::Weak::new())
}
}
pub(crate) struct VarHandlerOwner(Arc<AtomicBool>);
impl fmt::Debug for VarHandlerOwner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", Arc::strong_count(&self.0) - 1)?;
if self.0.load(std::sync::atomic::Ordering::Relaxed) {
write!(f, " perm")
} else {
Ok(())
}
}
}
impl VarHandlerOwner {
pub fn is_alive(&self) -> bool {
Arc::strong_count(&self.0) > 1 || self.0.load(std::sync::atomic::Ordering::Relaxed)
}
}
#[must_use = "var handles stops the behavior they represents on drop"]
#[derive(Clone, Default)]
pub struct VarHandles(smallvec::SmallVec<[VarHandle; 2]>);
impl VarHandles {
pub const fn dummy() -> Self {
VarHandles(smallvec::SmallVec::new_const())
}
pub fn is_dummy(&self) -> bool {
self.0.is_empty() || self.0.iter().all(VarHandle::is_dummy)
}
pub fn perm(self) {
for handle in self.0 {
handle.perm()
}
}
pub fn push(&mut self, other: VarHandle) -> &mut Self {
if !other.is_dummy() {
self.0.push(other);
}
self
}
pub fn clear(&mut self) {
self.0.clear()
}
}
impl FromIterator<VarHandle> for VarHandles {
fn from_iter<T: IntoIterator<Item = VarHandle>>(iter: T) -> Self {
VarHandles(iter.into_iter().filter(|h| !h.is_dummy()).collect())
}
}
impl<const N: usize> From<[VarHandle; N]> for VarHandles {
fn from(handles: [VarHandle; N]) -> Self {
handles.into_iter().collect()
}
}
impl Extend<VarHandle> for VarHandles {
fn extend<T: IntoIterator<Item = VarHandle>>(&mut self, iter: T) {
for handle in iter {
self.push(handle);
}
}
}
impl IntoIterator for VarHandles {
type Item = VarHandle;
type IntoIter = smallvec::IntoIter<[VarHandle; 2]>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl ops::Deref for VarHandles {
type Target = smallvec::SmallVec<[VarHandle; 2]>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ops::DerefMut for VarHandles {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<VarHandle> for VarHandles {
fn from(value: VarHandle) -> Self {
let mut r = VarHandles::dummy();
r.push(value);
r
}
}
#[cfg(feature = "type_names")]
fn value_type_name(var: &dyn VarImpl) -> &'static str {
var.value_type_name()
}
#[cfg(not(feature = "type_names"))]
#[inline(always)]
fn value_type_name(_: &dyn VarImpl) -> &'static str {
""
}