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#[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}