Skip to main content

shelly/
nested.rs

1use crate::{Context, Html, LiveView};
2use std::fmt;
3
4/// Stable server-owned identifier for a nested LiveView instance.
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub struct NestedLiveViewId(String);
7
8impl NestedLiveViewId {
9    pub fn new(id: impl Into<String>) -> Self {
10        Self(id.into())
11    }
12
13    pub fn as_str(&self) -> &str {
14        &self.0
15    }
16}
17
18impl fmt::Display for NestedLiveViewId {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        f.write_str(&self.0)
21    }
22}
23
24impl From<&str> for NestedLiveViewId {
25    fn from(value: &str) -> Self {
26        Self::new(value)
27    }
28}
29
30impl From<String> for NestedLiveViewId {
31    fn from(value: String) -> Self {
32        Self::new(value)
33    }
34}
35
36/// Runtime lifecycle state for one nested LiveView.
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum NestedLiveViewState {
39    /// Child is mounted and accepts scoped events.
40    Mounted,
41    /// Child keeps server state but does not accept browser events.
42    Suspended,
43    /// Child has terminated and no longer accepts events or renders.
44    Terminated,
45}
46
47/// Public snapshot of one nested LiveView runtime boundary.
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct NestedLiveViewSnapshot {
50    pub id: NestedLiveViewId,
51    pub target: String,
52    pub state: NestedLiveViewState,
53    pub route_path: String,
54}
55
56pub(crate) struct NestedLiveViewInstance {
57    pub(crate) view: Box<dyn LiveView>,
58    pub(crate) ctx: Context,
59    pub(crate) state: NestedLiveViewState,
60    pub(crate) last_render: Option<Html>,
61}
62
63impl NestedLiveViewInstance {
64    pub(crate) fn snapshot(&self, id: NestedLiveViewId) -> NestedLiveViewSnapshot {
65        NestedLiveViewSnapshot {
66            id,
67            target: self.ctx.target_dom_id().to_string(),
68            state: self.state,
69            route_path: self.ctx.route_path().to_string(),
70        }
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::{NestedLiveViewId, NestedLiveViewState};
77
78    #[test]
79    fn nested_live_view_id_round_trips() {
80        let id = NestedLiveViewId::new("sidebar");
81        assert_eq!(id.as_str(), "sidebar");
82        assert_eq!(id.to_string(), "sidebar");
83        assert_eq!(NestedLiveViewId::from("sidebar"), id);
84    }
85
86    #[test]
87    fn nested_live_view_state_is_copyable() {
88        let state = NestedLiveViewState::Mounted;
89        assert_eq!(state, NestedLiveViewState::Mounted);
90    }
91}