leptos_server/
lib.rs

1//! Utilities for communicating between the server and the client with Leptos.
2
3#![deny(missing_docs)]
4#![forbid(unsafe_code)]
5
6mod action;
7pub use action::*;
8use std::borrow::Borrow;
9mod local_resource;
10pub use local_resource::*;
11mod multi_action;
12pub use multi_action::*;
13mod once_resource;
14pub use once_resource::*;
15mod resource;
16pub use resource::*;
17mod shared;
18
19use base64::{engine::general_purpose::STANDARD_NO_PAD, DecodeError, Engine};
20/// Re-export of the `codee` crate.
21pub use codee;
22pub use shared::*;
23
24/// Encodes data into a string.
25pub trait IntoEncodedString {
26    /// Encodes the data.
27    fn into_encoded_string(self) -> String;
28}
29
30/// Decodes data from a string.
31pub trait FromEncodedStr {
32    /// The decoded data.
33    type DecodedType<'a>: Borrow<Self>;
34
35    /// The type of an error encountered during decoding.
36    type DecodingError;
37
38    /// Decodes the string.
39    fn from_encoded_str(
40        data: &str,
41    ) -> Result<Self::DecodedType<'_>, Self::DecodingError>;
42}
43
44impl IntoEncodedString for String {
45    fn into_encoded_string(self) -> String {
46        self
47    }
48}
49
50impl FromEncodedStr for str {
51    type DecodedType<'a> = &'a str;
52    type DecodingError = ();
53
54    fn from_encoded_str(
55        data: &str,
56    ) -> Result<Self::DecodedType<'_>, Self::DecodingError> {
57        Ok(data)
58    }
59}
60
61impl IntoEncodedString for Vec<u8> {
62    fn into_encoded_string(self) -> String {
63        STANDARD_NO_PAD.encode(self)
64    }
65}
66
67impl FromEncodedStr for [u8] {
68    type DecodedType<'a> = Vec<u8>;
69    type DecodingError = DecodeError;
70
71    fn from_encoded_str(
72        data: &str,
73    ) -> Result<Self::DecodedType<'_>, Self::DecodingError> {
74        STANDARD_NO_PAD.decode(data)
75    }
76}
77
78#[cfg(feature = "tachys")]
79mod view_implementations {
80    use crate::Resource;
81    use reactive_graph::traits::Read;
82    use std::future::Future;
83    use tachys::{
84        html::attribute::{any_attribute::AnyAttribute, Attribute},
85        hydration::Cursor,
86        reactive_graph::{RenderEffectState, Suspend, SuspendState},
87        ssr::StreamBuilder,
88        view::{
89            add_attr::AddAnyAttr, Position, PositionState, Render, RenderHtml,
90        },
91    };
92
93    impl<T, Ser> Render for Resource<T, Ser>
94    where
95        T: Render + Send + Sync + Clone,
96        Ser: Send + 'static,
97    {
98        type State = RenderEffectState<SuspendState<T>>;
99
100        fn build(self) -> Self::State {
101            (move || Suspend::new(async move { self.await })).build()
102        }
103
104        fn rebuild(self, state: &mut Self::State) {
105            (move || Suspend::new(async move { self.await })).rebuild(state)
106        }
107    }
108
109    impl<T, Ser> AddAnyAttr for Resource<T, Ser>
110    where
111        T: RenderHtml + Send + Sync + Clone,
112        Ser: Send + 'static,
113    {
114        type Output<SomeNewAttr: Attribute> = Box<
115            dyn FnMut() -> Suspend<
116                <T as AddAnyAttr>::Output<
117                    <SomeNewAttr::CloneableOwned as Attribute>::CloneableOwned,
118                >,
119            >
120            + Send
121        >;
122
123        fn add_any_attr<NewAttr: Attribute>(
124            self,
125            attr: NewAttr,
126        ) -> Self::Output<NewAttr>
127        where
128            Self::Output<NewAttr>: RenderHtml,
129        {
130            (move || Suspend::new(async move { self.await })).add_any_attr(attr)
131        }
132    }
133
134    impl<T, Ser> RenderHtml for Resource<T, Ser>
135    where
136        T: RenderHtml + Send + Sync + Clone,
137        Ser: Send + 'static,
138    {
139        type AsyncOutput = Option<T>;
140        type Owned = Self;
141
142        const MIN_LENGTH: usize = 0;
143
144        fn dry_resolve(&mut self) {
145            self.read();
146        }
147
148        fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send {
149            (move || Suspend::new(async move { self.await })).resolve()
150        }
151
152        fn to_html_with_buf(
153            self,
154            buf: &mut String,
155            position: &mut Position,
156            escape: bool,
157            mark_branches: bool,
158            extra_attrs: Vec<AnyAttribute>,
159        ) {
160            (move || Suspend::new(async move { self.await })).to_html_with_buf(
161                buf,
162                position,
163                escape,
164                mark_branches,
165                extra_attrs,
166            );
167        }
168
169        fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
170            self,
171            buf: &mut StreamBuilder,
172            position: &mut Position,
173            escape: bool,
174            mark_branches: bool,
175            extra_attrs: Vec<AnyAttribute>,
176        ) where
177            Self: Sized,
178        {
179            (move || Suspend::new(async move { self.await }))
180                .to_html_async_with_buf::<OUT_OF_ORDER>(
181                    buf,
182                    position,
183                    escape,
184                    mark_branches,
185                    extra_attrs,
186                );
187        }
188
189        fn hydrate<const FROM_SERVER: bool>(
190            self,
191            cursor: &Cursor,
192            position: &PositionState,
193        ) -> Self::State {
194            (move || Suspend::new(async move { self.await }))
195                .hydrate::<FROM_SERVER>(cursor, position)
196        }
197
198        fn into_owned(self) -> Self::Owned {
199            self
200        }
201    }
202}