lenra_app/
lib.rs

1use std::{
2    error::Error,
3    fmt::{self, Debug},
4    io::{self, Write},
5    str,
6};
7
8use listener::{Listener, ListenerRequest};
9use log::{error, warn};
10use manifest::Manifest;
11use resource::{Resource, ResourceRequest};
12use serde::{de::DeserializeOwned, Deserialize};
13use serde_json::Value;
14use view::{View, ViewRequest};
15
16pub mod api;
17pub mod components;
18pub mod listener;
19pub mod manifest;
20pub mod resource;
21pub mod view;
22
23pub type Result<T, E = Box<dyn Error>> = std::result::Result<T, E>;
24
25// Macros
26
27#[macro_export]
28macro_rules! from_value {
29    ($name:ident) => {
30        impl Into<$name> for serde_json::Value {
31            fn into(self) -> $name {
32                serde_json::from_value(self).unwrap()
33            }
34        }
35    };
36}
37
38#[macro_export]
39macro_rules! props {
40    ($name:ident) => {
41        impl From<$name> for crate::components::lenra::DefsProps {
42            fn from(value: $name) -> crate::components::lenra::DefsProps {
43                serde_json::to_value(value).unwrap().into()
44            }
45        }
46        impl From<$name> for crate::manifest::DefsProps {
47            fn from(value: $name) -> crate::manifest::DefsProps {
48                serde_json::to_value(value).unwrap().into()
49            }
50        }
51    };
52}
53
54// Components
55
56#[derive(Default)]
57pub struct LenraApp {
58    pub manifest: Manifest,
59    pub views: Vec<View>,
60    pub listeners: Vec<Listener>,
61    pub resources: Vec<Resource>,
62}
63
64impl LenraApp {
65    pub fn run(self) -> Result<()> {
66        env_logger::init();
67
68        let request = serde_json::from_reader(std::io::stdin());
69        self.handle(
70            request.unwrap_or(Request::Other(Value::Null)),
71            &mut io::stdout(),
72        )
73    }
74
75    pub(crate) fn handle<W: Write>(self, request: Request, writer: &mut W) -> Result<()> {
76        match request {
77            Request::View(view) => self.handle_view(view, writer)?,
78            Request::Listener(listener) => self.handle_listener(listener, writer)?,
79            Request::Resource(resource) => self.handle_resource(resource, writer)?,
80            Request::Other(req) => {
81                if req != Value::Null {
82                    warn!("Not managed request: {}", req);
83                }
84                write!(writer, "{}", serde_json::to_string(&self.manifest)?)?;
85            }
86        };
87        Ok(())
88    }
89
90    fn handle_view<W: Write>(self, request: ViewRequest, writer: &mut W) -> Result<()> {
91        let opt = self.views.iter().find(|&v| v.name() == request.name());
92        if let Some(view) = opt {
93            let result = view.handle(request)?;
94            write!(writer, "{}", result)?;
95        } else {
96            let message = format!("No view found for {}", request.name());
97            error!("{}", message);
98            return Err(Box::new(CustomError { message }));
99        };
100        Ok(())
101    }
102
103    fn handle_listener<W: Write>(self, request: ListenerRequest, _writer: &mut W) -> Result<()> {
104        let opt = self.listeners.iter().find(|&v| v.name() == request.name());
105        if let Some(listener) = opt {
106            listener.handle(request)
107        } else {
108            let message = format!("No listener found for {}", request.name());
109            error!("{}", message);
110            Err(Box::new(CustomError { message }))
111        }
112    }
113
114    fn handle_resource<W: Write>(self, request: ResourceRequest, writer: &mut W) -> Result<()> {
115        let opt = self.resources.iter().find(|&v| v.name() == request.name());
116        if let Some(resource) = opt {
117            let result = resource.handle(request)?;
118            writer.write_all(&result)?;
119        } else {
120            let message = format!("No resource found for {}", request.name());
121            error!("{}", message);
122            return Err(Box::new(CustomError { message }));
123        };
124        Ok(())
125    }
126}
127
128pub trait HandleParams<R> {
129    fn from_request(request: R) -> Self;
130}
131
132pub trait NamedRequest {
133    fn name(&self) -> String;
134}
135
136pub trait RequestHandler<Req, Res>: Sized
137where
138    Req: NamedRequest,
139{
140    fn name(&self) -> String;
141    fn handle(&self, request: Req) -> Result<Res>;
142    fn create(name: &str, build_fn: Box<dyn Fn(Req) -> Result<Res>>) -> Self;
143}
144
145pub trait Handler<Req, Res>: RequestHandler<Req, Res>
146where
147    Req: NamedRequest,
148{
149    fn new<P, F>(name: &str, build_fn: F) -> Self
150    where
151        P: HandleParams<Req>,
152        F: Fn(P) -> Result<Res> + 'static,
153    {
154        let boxed_fn: Box<dyn Fn(Req) -> Result<Res>> =
155            Box::new(move |request: Req| build_fn(P::from_request(request)));
156        Self::create(name, boxed_fn)
157    }
158}
159
160pub(crate) fn from_opt_value<T>(opt: Option<Value>) -> Result<Option<T>>
161where
162    T: DeserializeOwned + 'static,
163{
164    Ok(match opt {
165        Some(value) => match value.clone() {
166            Value::Null => None,
167            Value::Object(obj) => {
168                if obj.is_empty() {
169                    None
170                } else {
171                    Some(
172                        serde_json::from_value(value)
173                            .map_err(|err| Box::new(err) as Box<dyn std::error::Error>)?,
174                    )
175                }
176            }
177            _ => Some(
178                serde_json::from_value(value)
179                    .map_err(|err| Box::new(err) as Box<dyn std::error::Error>)?,
180            ),
181        },
182        None => None,
183    })
184}
185
186/** The application input */
187#[derive(Deserialize, Debug, PartialEq)]
188#[serde(untagged)]
189enum Request {
190    View(ViewRequest),
191    Listener(ListenerRequest),
192    Resource(ResourceRequest),
193    Other(Value),
194}
195
196#[derive(Debug)]
197struct CustomError {
198    message: String,
199}
200
201impl Error for CustomError {}
202
203impl fmt::Display for CustomError {
204    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205        write!(f, "{}", self.message)
206    }
207}
208
209pub trait ComponentBuilder<T>: Sized + Clone + std::fmt::Debug
210where
211    T: serde::ser::Serialize + std::convert::TryFrom<Self>,
212    T::Error: std::fmt::Display + std::fmt::Debug,
213{
214    fn build(self) -> T {
215        T::try_from(self).unwrap()
216    }
217}
218
219#[cfg(test)]
220mod test {
221
222    use serde_json::json;
223
224    use crate::view::{ViewParams, ViewResponseGenerator};
225
226    use super::*;
227
228    #[test]
229    fn simple_view() {
230        let app = LenraApp {
231            // manifest: Manifest {
232            //     root_view: "test".into(),
233            // },
234            views: vec![View::new("test", |_: ViewParams| {
235                Ok(json!({"type": "text", "value": "test"}).gen())
236            })],
237            ..Default::default()
238        };
239        let request = ViewRequest {
240            view: "test".into(),
241            data: None,
242            props: None,
243            context: None,
244        };
245        let mut buf = Vec::new();
246        app.handle(Request::View(request), &mut buf).unwrap();
247        let result = String::from_utf8(buf).unwrap();
248        assert_eq!(result, r#"{"type":"text","value":"test"}"#);
249    }
250
251    #[test]
252    #[should_panic]
253    fn unkown_view() {
254        let app = LenraApp {
255            // manifest: Manifest {
256            //     root_view: "test".into(),
257            // },
258            views: vec![View::new("test", |_: ViewParams| {
259                Ok(json!({"type": "text", "value": "test"}).gen())
260            })],
261            ..Default::default()
262        };
263        let request = ViewRequest {
264            view: "test2".into(),
265            data: None,
266            props: None,
267            context: None,
268        };
269        let mut buf = Vec::new();
270        app.handle(Request::View(request), &mut buf).unwrap();
271    }
272}