Skip to main content

react_rs_core/
component.rs

1use 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}