1use reactive_graph::{
2 actions::{Action, ArcAction},
3 owner::use_context,
4 traits::DefinedAt,
5};
6use server_fn::{
7 error::{FromServerFnError, ServerFnUrlError},
8 ServerFn,
9};
10use std::{ops::Deref, panic::Location, sync::Arc};
11
12#[derive(Clone, Debug, PartialEq, Eq)]
17pub struct ServerActionError {
18 path: Arc<str>,
19 err: Arc<str>,
20}
21
22impl ServerActionError {
23 pub fn new(path: &str, err: &str) -> Self {
25 Self {
26 path: path.into(),
27 err: err.into(),
28 }
29 }
30
31 pub fn path(&self) -> &str {
33 &self.path
34 }
35
36 pub fn err(&self) -> &str {
38 &self.err
39 }
40}
41
42pub struct ArcServerAction<S>
44where
45 S: ServerFn + 'static,
46 S::Output: 'static,
47{
48 inner: ArcAction<S, Result<S::Output, S::Error>>,
49 #[cfg(any(debug_assertions, leptos_debuginfo))]
50 defined_at: &'static Location<'static>,
51}
52
53impl<S> ArcServerAction<S>
54where
55 S: ServerFn + Clone + Send + Sync + 'static,
56 S::Output: Send + Sync + 'static,
57 S::Error: Send + Sync + 'static,
58 S::Error: FromServerFnError,
59{
60 #[track_caller]
62 pub fn new() -> Self {
63 let err = use_context::<ServerActionError>().and_then(|error| {
64 (error.path() == S::PATH)
65 .then(|| ServerFnUrlError::<S::Error>::decode_err(error.err()))
66 .map(Err)
67 });
68 Self {
69 inner: ArcAction::new_with_value(err, |input: &S| {
70 S::run_on_client(input.clone())
71 }),
72 #[cfg(any(debug_assertions, leptos_debuginfo))]
73 defined_at: Location::caller(),
74 }
75 }
76}
77
78impl<S> Deref for ArcServerAction<S>
79where
80 S: ServerFn + 'static,
81 S::Output: 'static,
82{
83 type Target = ArcAction<S, Result<S::Output, S::Error>>;
84
85 fn deref(&self) -> &Self::Target {
86 &self.inner
87 }
88}
89
90impl<S> Clone for ArcServerAction<S>
91where
92 S: ServerFn + 'static,
93 S::Output: 'static,
94{
95 fn clone(&self) -> Self {
96 Self {
97 inner: self.inner.clone(),
98 #[cfg(any(debug_assertions, leptos_debuginfo))]
99 defined_at: self.defined_at,
100 }
101 }
102}
103
104impl<S> Default for ArcServerAction<S>
105where
106 S: ServerFn + Clone + Send + Sync + 'static,
107 S::Output: Send + Sync + 'static,
108 S::Error: Send + Sync + 'static,
109{
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115impl<S> DefinedAt for ArcServerAction<S>
116where
117 S: ServerFn + 'static,
118 S::Output: 'static,
119{
120 fn defined_at(&self) -> Option<&'static Location<'static>> {
121 #[cfg(any(debug_assertions, leptos_debuginfo))]
122 {
123 Some(self.defined_at)
124 }
125 #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
126 {
127 None
128 }
129 }
130}
131
132pub struct ServerAction<S>
134where
135 S: ServerFn + 'static,
136 S::Output: 'static,
137{
138 inner: Action<S, Result<S::Output, S::Error>>,
139 #[cfg(any(debug_assertions, leptos_debuginfo))]
140 defined_at: &'static Location<'static>,
141}
142
143impl<S> ServerAction<S>
144where
145 S: ServerFn + Send + Sync + Clone + 'static,
146 S::Output: Send + Sync + 'static,
147 S::Error: Send + Sync + 'static,
148{
149 pub fn new() -> Self {
151 let err = use_context::<ServerActionError>().and_then(|error| {
152 (error.path() == S::PATH)
153 .then(|| ServerFnUrlError::<S::Error>::decode_err(error.err()))
154 .map(Err)
155 });
156 Self {
157 inner: Action::new_with_value(err, |input: &S| {
158 S::run_on_client(input.clone())
159 }),
160 #[cfg(any(debug_assertions, leptos_debuginfo))]
161 defined_at: Location::caller(),
162 }
163 }
164}
165
166impl<S> Clone for ServerAction<S>
167where
168 S: ServerFn + 'static,
169 S::Output: 'static,
170{
171 fn clone(&self) -> Self {
172 *self
173 }
174}
175
176impl<S> Copy for ServerAction<S>
177where
178 S: ServerFn + 'static,
179 S::Output: 'static,
180{
181}
182
183impl<S> Deref for ServerAction<S>
184where
185 S: ServerFn + Clone + Send + Sync + 'static,
186 S::Output: Send + Sync + 'static,
187 S::Error: Send + Sync + 'static,
188{
189 type Target = Action<S, Result<S::Output, S::Error>>;
190
191 fn deref(&self) -> &Self::Target {
192 &self.inner
193 }
194}
195
196impl<S> From<ServerAction<S>> for Action<S, Result<S::Output, S::Error>>
197where
198 S: ServerFn + 'static,
199 S::Output: 'static,
200{
201 fn from(value: ServerAction<S>) -> Self {
202 value.inner
203 }
204}
205
206impl<S> Default for ServerAction<S>
207where
208 S: ServerFn + Clone + Send + Sync + 'static,
209 S::Output: Send + Sync + 'static,
210 S::Error: Send + Sync + 'static,
211{
212 fn default() -> Self {
213 Self::new()
214 }
215}
216
217impl<S> DefinedAt for ServerAction<S>
218where
219 S: ServerFn + 'static,
220 S::Output: 'static,
221{
222 fn defined_at(&self) -> Option<&'static Location<'static>> {
223 #[cfg(any(debug_assertions, leptos_debuginfo))]
224 {
225 Some(self.defined_at)
226 }
227 #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
228 {
229 None
230 }
231 }
232}