avalanche/
lib.rs

1/// Provides useful hooks and supporting utilities.
2pub mod hooks;
3/// Holds platform-specific rendering interfaces.
4pub mod renderer;
5/// A reference-counted interior-mutable type designed to reduce runtime borrow rule violations.
6pub mod shared;
7/// Holds types and macros used for propogating and tracking data updates.
8pub mod tracked;
9/// A `Vec`-backed implementation of a tree, with a relatively friendly mutation api.
10pub(crate) mod tree;
11/// An in-memory representation of the current component tree.
12#[doc(hidden)]
13pub mod vdom;
14
15use downcast_rs::{impl_downcast, Downcast};
16use std::rc::Rc;
17
18use renderer::NativeType;
19use shared::Shared;
20use tree::NodeId;
21use vdom::{VDom, VNode};
22
23pub use hooks::{state, vec, Context};
24pub use tracked::Tracked;
25
26/// An attribute macro used to define components.
27///
28/// # Basic usage
29/// The macro must be applied to a function that returns a [View](View).
30/// It generates a `struct` implementing [Component](Component) with the name of the input function, and a builder struct.
31/// The function's name should start with a capital ASCII character.
32///
33/// The function can optionally take parameters. Parameter types must be `Clone` and `'static`.
34/// Parameters must have concrete types: they cannot use the `impl Trait`
35/// syntax. Components cannot be `async` or generic.
36///
37/// A component must return a [View] describing what it will render.
38/// Components are invoked with the same syntax as function calls, except with
39/// the macro `!` after the type name:
40/// ```rust
41/// use avalanche::{component, tracked, View};
42/// use avalanche_web::components::{H1, Text};
43///
44/// const class: &str = "hello-world";
45///
46/// #[component]
47/// pub fn HelloWorld(name: String) -> View {
48///     H1!(
49///         id: class,
50///         class: class,
51///         child: Text!(format!("Hi there, {}!", tracked!(name)))
52///     )
53/// }
54/// ```
55///
56/// In the case of `H1`, all of its parameters are optional and have default values, but for components generated by this
57/// macro, all parameters must be provided, or component instantiation will panic at runtime. In the future, this will
58/// instead be a compile-time error.
59///
60/// Note that all macro invocations beginning with a capital ASCII character will be interpreted as component invocations
61/// within the function. If you need to invoke a macro beginning with a capital letter, consider using it with an alias
62/// beginning with `_`.
63///
64/// ## Tracked values
65/// Parameter values are passed into a function with the [Tracked](Tracked) wrapper. More details on how that works is found in
66/// the [tracked](tracked!) macro documentation.
67/// 
68/// ## Avoid side effects and mutability
69/// `avalanche` considers a parameter updated if one of the parameters or hooks that influence it change, but
70/// a function like [rand::thread_rng](https://docs.rs/rand/0.8/rand/fn.thread_rng.html) has a different value on every call
71/// despite having no parameter or hook dependencies.
72/// Using values from functions and methods that are not pure or mutate values will lead to missed updates. Instead, create new variables: 
73/// use `let string = string + " appended"` instead of `string += "appended"`. 
74/// If you need to use mutation in a component, including interior mutability, use the [state](state) hook.
75///
76/// ## Eschew third-party macros
77/// Unfortunately, macros are syntax extensions and `component` cannot keep track of tracked variables in most of them.
78/// All `std` macros (like [vec!](std::vec!()) and [format!](std::format!())) and `avalanche` macros (like [enclose!()])
79/// work well, but any others may lead to parameters being incorrectly marked as not updated.
80#[doc(inline)]
81pub use avalanche_macro::component;
82
83/// Takes a list of identifiers terminated by a semicolon and expression. Each identifier is
84/// cloned, and made available to the expression. The macro evaluates to that expression.
85/// This is useful for passing things like state and setters to multiple component props.
86/// # Example
87/// ```rust
88/// # use avalanche::enclose;
89/// let message = "Enclose me!".to_owned();
90/// let closure1 = enclose!(message; move || println!("{}", message));
91/// let closure2 = enclose!(message; move || eprintln!("{}", message));
92/// ```
93#[macro_export]
94macro_rules! enclose {
95    ( $( $x:ident ),*; $y:expr ) => {
96        {
97            $(let $x = $x.clone();)*
98            $y
99        }
100    };
101}
102
103/// For internal use only, does not obey semver and is unsupported
104/// A hack used to work around a seeming syn limitation
105/// syn will interpret some macro calls as items rather than expressions
106/// syn expects an item to be replaced by another, so when parsing Component! type macros,
107/// `#[component]` will replace that call with an expression within this macro
108/// to syn, this is replacing one macro item with another
109#[doc(hidden)]
110#[macro_export]
111macro_rules! __internal_identity {
112    ($e:expr) => {
113        $e
114    };
115}
116
117/// A reference-counted type that holds an instance of a component.
118/// Component functions must return a [`View`].
119#[derive(Clone)]
120pub struct View {
121    rc: Rc<dyn DynComponent>,
122}
123
124impl View {
125    fn new<T: DynComponent>(val: T) -> Self {
126        Self { rc: Rc::new(val) }
127    }
128}
129
130impl std::ops::Deref for View {
131    type Target = dyn DynComponent;
132
133    fn deref(&self) -> &dyn DynComponent {
134        &*self.rc
135    }
136}
137
138impl<T: DynComponent> From<T> for View {
139    fn from(val: T) -> Self {
140        Self::new(val)
141    }
142}
143
144impl<T: DynComponent> From<Option<T>> for View {
145    fn from(val: Option<T>) -> Self {
146        match val {
147            Some(val) => View::new(val),
148            None => View::new(()),
149        }
150    }
151}
152
153impl From<Option<View>> for View {
154    fn from(val: Option<View>) -> Self {
155        match val {
156            Some(val) => val,
157            None => View::new(()),
158        }
159    }
160}
161
162/// The trait representing a component. 
163/// 
164/// Users should not implement this trait manually but instead use the `component` attribute.
165/// However, native component implementations may need to use manual component implementations.
166pub trait Component: 'static {
167    type Builder;
168
169    fn render(&self, ctx: Context) -> View;
170
171    fn updated(&self) -> bool;
172
173    fn native_type(&self) -> Option<NativeType> {
174        None
175    }
176
177    fn location(&self) -> Option<(u32, u32)> {
178        None
179    }
180
181    fn key(&self) -> Option<&str> {
182        None
183    }
184}
185
186impl Component for () {
187    // TODO: make ! when never stabilizes
188    type Builder = ();
189
190    fn render(&self, _: Context) -> View {
191        unreachable!()
192    }
193    fn updated(&self) -> bool {
194        false
195    }
196}
197
198/// An internal trait implemented for all [`Component`]s. This should not be
199/// implemented manually.
200#[doc(hidden)]
201pub trait DynComponent: Downcast + 'static {
202    fn render(&self, ctx: Context) -> View;
203
204    fn native_type(&self) -> Option<NativeType>;
205
206    fn updated(&self) -> bool;
207
208    fn location(&self) -> Option<(u32, u32)>;
209
210    fn key(&self) -> Option<&str>;
211}
212
213impl_downcast!(DynComponent);
214
215impl<T: Component> DynComponent for T {
216    fn render(&self, ctx: Context) -> View {
217        Component::render(self, ctx)
218    }
219
220    fn native_type(&self) -> Option<NativeType> {
221        Component::native_type(self)
222    }
223
224    fn updated(&self) -> bool {
225        Component::updated(self)
226    }
227
228    fn location(&self) -> Option<(u32, u32)> {
229        Component::location(self)
230    }
231
232    fn key(&self) -> Option<&str> {
233        Component::key(self)
234    }
235}
236
237#[doc(hidden)]
238#[derive(Copy, Clone)]
239pub struct ComponentNodeId {
240    pub(crate) id: NodeId<VNode>,
241}
242
243impl From<NodeId<VNode>> for ComponentNodeId {
244    fn from(node: NodeId<VNode>) -> Self {
245        Self { id: node }
246    }
247}
248
249#[doc(hidden)]
250#[derive(Copy, Clone)]
251/// Internal data structure that stores what tree a component
252/// belongs to, and its position within it
253pub struct ComponentPos<'a> {
254    /// Shared value ONLY for passing to UseState
255    /// within the render function this value is mutably borrowed,
256    /// so exec and exec_mut will panic
257    pub node_id: ComponentNodeId,
258    /// Shared container to the VDom of which the [`vnode`] is a part.
259    pub vdom: &'a Shared<VDom>,
260}