async_component_core/
lib.rs

1#![doc = include_str!("../README.md")]
2
3#[doc(hidden)]
4#[path = "exports.rs"]
5pub mod __private;
6pub mod context;
7
8use context::{with_current_context, StateContext};
9use futures_core::Stream;
10
11use std::{
12    ops::{Deref, DerefMut},
13    pin::Pin,
14    task::Poll,
15};
16
17/// Core trait
18pub trait AsyncComponent {
19    fn update_component(&mut self);
20}
21
22/// State trait
23///
24/// Returns output if state is updated
25pub trait State {
26    type Output;
27
28    fn update(this: &mut Self) -> Option<Self::Output>;
29}
30
31/// Track change of value and signal to [`StateContext`].
32/// This struct has no method and implements [`Deref`], [`DerefMut`].
33/// When inner value is mutable dereferenced, it is marked changed and send signal.
34/// This will also send signal when the cell is constructed or dropped.
35#[derive(Debug)]
36pub struct StateCell<T> {
37    changed: bool,
38    inner: T,
39}
40
41impl<T> StateCell<T> {
42    /// Create new [`StateCell`]
43    pub fn new(inner: T) -> Self {
44        with_current_context(StateContext::signal);
45
46        Self {
47            changed: true,
48            inner,
49        }
50    }
51
52    /// Invalidate this [`StateCell`].
53    /// Send signal to context.
54    pub fn invalidate(this: &mut Self) {
55        if !this.changed {
56            this.changed = true;
57        }
58
59        with_current_context(StateContext::signal);
60    }
61}
62
63impl<T> Deref for StateCell<T> {
64    type Target = T;
65
66    fn deref(&self) -> &Self::Target {
67        &self.inner
68    }
69}
70
71impl<T> DerefMut for StateCell<T> {
72    fn deref_mut(&mut self) -> &mut Self::Target {
73        StateCell::invalidate(self);
74
75        &mut self.inner
76    }
77}
78
79impl<T> State for StateCell<T> {
80    type Output = ();
81
82    fn update(this: &mut Self) -> Option<Self::Output> {
83        if this.changed {
84            this.changed = false;
85            Some(())
86        } else {
87            None
88        }
89    }
90}
91
92impl<T> From<T> for StateCell<T> {
93    fn from(inner: T) -> Self {
94        Self::new(inner)
95    }
96}
97
98impl<T: Default> Default for StateCell<T> {
99    fn default() -> Self {
100        Self::new(Default::default())
101    }
102}
103
104impl<T> Drop for StateCell<T> {
105    fn drop(&mut self) {
106        with_current_context(StateContext::signal);
107    }
108}
109
110/// State which polls inner stream
111#[derive(Debug)]
112pub struct StreamCell<T> {
113    inner: T,
114}
115
116impl<T: Stream> StreamCell<T> {
117    pub fn new(inner: T) -> Self {
118        with_current_context(StateContext::signal);
119        Self { inner }
120    }
121}
122
123impl<T: Stream + Unpin> State for StreamCell<T> {
124    type Output = T::Item;
125
126    fn update(this: &mut Self) -> Option<Self::Output> {
127        with_current_context(|cx| {
128            match Pin::new(&mut this.inner).poll_next(&mut cx.task_context()) {
129                Poll::Ready(Some(output)) => Some(output),
130                _ => None,
131            }
132        })
133    }
134}
135
136impl<T: Stream> From<T> for StreamCell<T> {
137    fn from(inner: T) -> Self {
138        Self::new(inner)
139    }
140}
141
142impl<T: Stream + Default> Default for StreamCell<T> {
143    fn default() -> Self {
144        Self::new(Default::default())
145    }
146}
147
148impl<T> Drop for StreamCell<T> {
149    fn drop(&mut self) {
150        with_current_context(StateContext::signal);
151    }
152}