1#![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};
20pub use codee;
22pub use shared::*;
23
24pub trait IntoEncodedString {
26 fn into_encoded_string(self) -> String;
28}
29
30pub trait FromEncodedStr {
32 type DecodedType<'a>: Borrow<Self>;
34
35 type DecodingError;
37
38 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}