react_rs_core/
component.rs1use std::marker::PhantomData;
2
3pub trait IntoView {
4 type View;
5 fn into_view(self) -> Self::View;
6}
7
8impl<T> IntoView for T
9where
10 T: Sized,
11{
12 type View = T;
13 fn into_view(self) -> Self::View {
14 self
15 }
16}
17
18pub struct Component<P, F, V>
19where
20 F: Fn(P) -> V,
21 V: IntoView,
22{
23 render: F,
24 _phantom: PhantomData<P>,
25}
26
27impl<P, F, V> Component<P, F, V>
28where
29 F: Fn(P) -> V,
30 V: IntoView,
31{
32 pub fn new(render: F) -> Self {
33 Self {
34 render,
35 _phantom: PhantomData,
36 }
37 }
38
39 pub fn call(&self, props: P) -> V::View {
40 (self.render)(props).into_view()
41 }
42}
43
44pub fn component<P, F, V>(render: F) -> Component<P, F, V>
45where
46 F: Fn(P) -> V,
47 V: IntoView,
48{
49 Component::new(render)
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn test_function_component() {
58 fn greeting(name: String) -> String {
59 format!("Hello, {}!", name)
60 }
61
62 let comp = component(greeting);
63 let result = comp.call("World".to_string());
64 assert_eq!(result, "Hello, World!");
65 }
66
67 #[test]
68 fn test_component_with_props_struct() {
69 #[derive(Clone)]
70 struct ButtonProps {
71 label: String,
72 disabled: bool,
73 }
74
75 fn button_view(props: ButtonProps) -> String {
76 if props.disabled {
77 format!("[{}] (disabled)", props.label)
78 } else {
79 format!("[{}]", props.label)
80 }
81 }
82
83 let button = component(button_view);
84
85 let result = button.call(ButtonProps {
86 label: "Click me".to_string(),
87 disabled: false,
88 });
89 assert_eq!(result, "[Click me]");
90
91 let result_disabled = button.call(ButtonProps {
92 label: "Submit".to_string(),
93 disabled: true,
94 });
95 assert_eq!(result_disabled, "[Submit] (disabled)");
96 }
97
98 #[test]
99 fn test_closure_component() {
100 let prefix = "Greeting: ";
101 let greet = component(move |name: String| format!("{}{}", prefix, name));
102
103 assert_eq!(greet.call("Alice".to_string()), "Greeting: Alice");
104 }
105}