lenra_app/
view.rs

1use serde::{de::DeserializeOwned, Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::{
5    components::lenra::LenraComponent, from_opt_value, HandleParams, Handler, NamedRequest,
6    RequestHandler, Result,
7};
8
9/** Unknown view request */
10#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
11pub struct ViewRequest {
12    pub view: String,
13    pub data: Option<Value>,
14    pub props: Option<Value>,
15    pub context: Option<Context>,
16}
17
18impl NamedRequest for ViewRequest {
19    fn name(&self) -> String {
20        self.view.clone()
21    }
22}
23
24pub struct ViewParams<D = Value, P = Value>
25where
26    D: DeserializeOwned + 'static,
27    P: DeserializeOwned + 'static,
28{
29    pub data: Option<D>,
30    pub props: Option<P>,
31    pub context: Option<Context>,
32}
33
34impl<D, P> HandleParams<ViewRequest> for ViewParams<D, P>
35where
36    D: DeserializeOwned + 'static,
37    P: DeserializeOwned + 'static,
38{
39    fn from_request(request: ViewRequest) -> Self {
40        ViewParams {
41            data: from_opt_value(request.data).unwrap(),
42            props: from_opt_value(request.props).unwrap(),
43            context: request.context,
44        }
45    }
46}
47
48pub trait ViewResponseGenerator: Serialize {
49    fn gen(&self) -> String {
50        serde_json::to_string(self).unwrap()
51    }
52}
53
54impl ViewResponseGenerator for Value {}
55
56impl ViewResponseGenerator for LenraComponent {}
57
58pub type ViewResponse = String;
59
60pub type ViewBuilder = Box<dyn Fn(ViewRequest) -> Result<String>>;
61
62pub struct View {
63    name: String,
64    build_fn: ViewBuilder,
65}
66
67impl RequestHandler<ViewRequest, ViewResponse> for View {
68    fn name(&self) -> String {
69        self.name.clone()
70    }
71
72    fn handle(&self, request: ViewRequest) -> Result<ViewResponse> {
73        (self.build_fn)(request)
74    }
75
76    fn create(name: &str, build_fn: ViewBuilder) -> Self {
77        View {
78            name: name.to_string(),
79            build_fn,
80        }
81    }
82}
83
84impl Handler<ViewRequest, ViewResponse> for View {}
85
86#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
87#[serde(rename_all = "camelCase")]
88pub struct Context {
89    pub screen_size: Option<ScreenSize>,
90}
91
92#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
93pub struct ScreenSize {
94    pub width: Option<u16>,
95    pub height: Option<u16>,
96}
97
98#[cfg(test)]
99mod test {
100
101    use serde_json::json;
102
103    use crate::{
104        components::{lenra::*, listener},
105        props, ComponentBuilder,
106    };
107
108    use super::*;
109
110    #[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
111    struct TestProps {
112        pub name: String,
113    }
114
115    props!(TestProps);
116
117    #[test]
118    fn parse_view_request_empty_props() {
119        let request: ViewRequest =
120            serde_json::from_str(r#"{"context":{},"data":[],"props":{},"view":"lenra:main"}"#)
121                .unwrap();
122        let view = super::View::new(
123            "test",
124            Box::new(|params: ViewParams<Value, TestProps>| {
125                let component: LenraComponent = text("test").into();
126                assert!(params.props.is_none());
127                Ok(component.gen())
128            }),
129        );
130        assert_eq!(
131            view.handle(request).unwrap(),
132            r#"{"_type":"text","value":"test"}"#
133        );
134    }
135
136    #[test]
137    fn simple_lenra_view_from_component() {
138        let view = super::View::create(
139            "test",
140            Box::new(|_| {
141                let component: LenraComponent = text("test").into();
142                Ok(component.gen())
143            }),
144        );
145        let request = ViewRequest {
146            view: "test".into(),
147            data: None,
148            props: None,
149            context: None,
150        };
151        assert_eq!(
152            view.handle(request).unwrap(),
153            r#"{"_type":"text","value":"test"}"#
154        );
155    }
156
157    #[test]
158    fn simple_json_view() {
159        let view = super::View::create(
160            "test",
161            Box::new(|_| {
162                let response = json!({
163                    "myField": "test",
164                    "onEvent": listener("name").build(),
165                });
166                Ok(response.gen())
167            }),
168        );
169        let request = ViewRequest {
170            view: "test".into(),
171            data: None,
172            props: None,
173            context: None,
174        };
175        assert_eq!(
176            view.handle(request).unwrap(),
177            r#"{"myField":"test","onEvent":{"_type":"listener","name":"name"}}"#
178        );
179    }
180}