1use super::attribute::{
2 maybe_next_attr_erasure_macros::next_attr_output_type, Attribute,
3 NextAttribute,
4};
5use crate::{
6 html::attribute::{
7 maybe_next_attr_erasure_macros::next_attr_combine, NamedAttributeKey,
8 },
9 prelude::AddAnyAttr,
10 view::{Position, ToTemplate},
11};
12use send_wrapper::SendWrapper;
13use std::{marker::PhantomData, sync::Arc};
14
15pub trait DirectiveAttribute<T, P, D>
18where
19 D: IntoDirective<T, P>,
20{
21 type Output;
23
24 fn directive(self, handler: D, param: P) -> Self::Output;
27}
28
29impl<V, T, P, D> DirectiveAttribute<T, P, D> for V
30where
31 V: AddAnyAttr,
32 D: IntoDirective<T, P>,
33 P: Clone + 'static,
34 T: 'static,
35{
36 type Output = <Self as AddAnyAttr>::Output<Directive<T, D, P>>;
37
38 fn directive(self, handler: D, param: P) -> Self::Output {
39 self.add_any_attr(directive(handler, param))
40 }
41}
42
43#[inline(always)]
46pub fn directive<T, P, D>(handler: D, param: P) -> Directive<T, D, P>
47where
48 D: IntoDirective<T, P>,
49{
50 Directive((!cfg!(feature = "ssr")).then(|| {
51 SendWrapper::new(DirectiveInner {
52 handler,
53 param,
54 t: PhantomData,
55 })
56 }))
57}
58
59#[derive(Debug)]
61pub struct Directive<T, D, P>(Option<SendWrapper<DirectiveInner<T, D, P>>>);
62
63impl<T, D, P> Clone for Directive<T, D, P>
64where
65 P: Clone + 'static,
66 D: Clone,
67{
68 fn clone(&self) -> Self {
69 Self(self.0.clone())
70 }
71}
72
73#[derive(Debug)]
74struct DirectiveInner<T, D, P> {
75 handler: D,
76 param: P,
77 t: PhantomData<T>,
78}
79
80impl<T, D, P> Clone for DirectiveInner<T, D, P>
81where
82 P: Clone + 'static,
83 D: Clone,
84{
85 fn clone(&self) -> Self {
86 Self {
87 handler: self.handler.clone(),
88 param: self.param.clone(),
89 t: PhantomData,
90 }
91 }
92}
93
94impl<T, P, D> Attribute for Directive<T, D, P>
95where
96 D: IntoDirective<T, P>,
97 P: Clone + 'static, T: 'static,
99{
100 const MIN_LENGTH: usize = 0;
101
102 type AsyncOutput = Self;
103 type State = crate::renderer::types::Element;
104 type Cloneable = Directive<T, D::Cloneable, P>;
105 type CloneableOwned = Directive<T, D::Cloneable, P>;
106
107 fn html_len(&self) -> usize {
108 0
109 }
110
111 fn to_html(
112 self,
113 _buf: &mut String,
114 _class: &mut String,
115 _style: &mut String,
116 _inner_html: &mut String,
117 ) {
118 }
119
120 fn hydrate<const FROM_SERVER: bool>(
121 self,
122 el: &crate::renderer::types::Element,
123 ) -> Self::State {
124 let inner = self.0.expect(super::FEATURE_CONFLICT_DIAGNOSTIC).take();
125 inner.handler.run(el.clone(), inner.param);
126 el.clone()
127 }
128
129 fn build(self, el: &crate::renderer::types::Element) -> Self::State {
130 let inner = self.0.expect(super::FEATURE_CONFLICT_DIAGNOSTIC).take();
131 inner.handler.run(el.clone(), inner.param);
132 el.clone()
133 }
134
135 fn rebuild(self, state: &mut Self::State) {
136 let inner = self.0.expect(super::FEATURE_CONFLICT_DIAGNOSTIC).take();
137 inner.handler.run(state.clone(), inner.param);
138 }
139
140 fn into_cloneable(self) -> Self::Cloneable {
141 self.into_cloneable_owned()
142 }
143
144 fn into_cloneable_owned(self) -> Self::CloneableOwned {
145 let inner = self.0.map(|inner| {
146 let DirectiveInner { handler, param, t } = inner.take();
147 SendWrapper::new(DirectiveInner {
148 handler: handler.into_cloneable(),
149 param,
150 t,
151 })
152 });
153 Directive(inner)
154 }
155
156 fn dry_resolve(&mut self) {}
157
158 async fn resolve(self) -> Self::AsyncOutput {
159 self
160 }
161
162 fn keys(&self) -> Vec<NamedAttributeKey> {
163 vec![]
164 }
165}
166
167impl<T, D, P> NextAttribute for Directive<T, D, P>
168where
169 D: IntoDirective<T, P>,
170 P: Clone + 'static,
171 T: 'static,
172{
173 next_attr_output_type!(Self, NewAttr);
174
175 fn add_any_attr<NewAttr: Attribute>(
176 self,
177 new_attr: NewAttr,
178 ) -> Self::Output<NewAttr> {
179 next_attr_combine!(self, new_attr)
180 }
181}
182
183impl<T, D, P> ToTemplate for Directive<T, D, P> {
184 const CLASS: &'static str = "";
185
186 fn to_template(
187 _buf: &mut String,
188 _class: &mut String,
189 _style: &mut String,
190 _inner_html: &mut String,
191 _position: &mut Position,
192 ) {
193 }
194}
195
196pub trait IntoDirective<T: ?Sized, P> {
243 type Cloneable: IntoDirective<T, P> + Clone + 'static;
245
246 fn run(&self, el: crate::renderer::types::Element, param: P);
248
249 fn into_cloneable(self) -> Self::Cloneable;
251}
252
253impl<F> IntoDirective<(crate::renderer::types::Element,), ()> for F
254where
255 F: Fn(crate::renderer::types::Element) + 'static,
256{
257 type Cloneable = Arc<dyn Fn(crate::renderer::types::Element)>;
258
259 fn run(&self, el: crate::renderer::types::Element, _: ()) {
260 self(el)
261 }
262
263 fn into_cloneable(self) -> Self::Cloneable {
264 Arc::new(self)
265 }
266}
267
268impl IntoDirective<(crate::renderer::types::Element,), ()>
269 for Arc<dyn Fn(crate::renderer::types::Element)>
270{
271 type Cloneable = Arc<dyn Fn(crate::renderer::types::Element)>;
272
273 fn run(&self, el: crate::renderer::types::Element, _: ()) {
274 self(el)
275 }
276
277 fn into_cloneable(self) -> Self::Cloneable {
278 self
279 }
280}
281
282impl<F, P> IntoDirective<(crate::renderer::types::Element, P), P> for F
283where
284 F: Fn(crate::renderer::types::Element, P) + 'static,
285 P: 'static,
286{
287 type Cloneable = Arc<dyn Fn(crate::renderer::types::Element, P)>;
288
289 fn run(&self, el: crate::renderer::types::Element, param: P) {
290 self(el, param);
291 }
292
293 fn into_cloneable(self) -> Self::Cloneable {
294 Arc::new(self)
295 }
296}
297
298impl<P> IntoDirective<(crate::renderer::types::Element, P), P>
299 for Arc<dyn Fn(crate::renderer::types::Element, P)>
300where
301 P: 'static,
302{
303 type Cloneable = Arc<dyn Fn(crate::renderer::types::Element, P)>;
304
305 fn run(&self, el: crate::renderer::types::Element, param: P) {
306 self(el, param)
307 }
308
309 fn into_cloneable(self) -> Self::Cloneable {
310 self
311 }
312}