raspberry_web/
app.rs

1use crate::handlers::{CheckGpioLevel, DbExecutor, GpioId, SetGpioLevel};
2use crate::models;
3use crate::rpi;
4use actix::Addr;
5use actix_web::Error as actixError;
6use actix_web::{http, middleware, App, AsyncResponder, FutureResponse, HttpResponse, Path, State};
7use futures::{future, Future};
8
9/// State with DbExecutor address
10pub struct AppState {
11    pub db: Addr<DbExecutor>,
12    pub gpio_arc_mutex: rpi::GpioArcMutex,
13}
14
15/// Get status of GPIO
16pub fn gpio_status_route(
17    (req, state): (Path<i32>, State<AppState>),
18) -> FutureResponse<HttpResponse> {
19    state
20        .db
21        .send(GpioId {
22            gpio_id: req.into_inner(),
23        })
24        .from_err()
25        .and_then(|res| match res {
26            Ok(user) => Ok(HttpResponse::Ok().json(user)),
27            Err(err) => {
28                let err_string = err.to_string();
29                let mut response = HttpResponse::from_error(err);
30                response.set_body(err_string);
31                Ok(response)
32            }
33        })
34        .responder()
35}
36
37/// Set GPIO level to HIGH or LOW
38pub fn set_gpio_level_route(
39    (req, state): (Path<(i32, String)>, State<AppState>),
40) -> FutureResponse<HttpResponse> {
41    let path_gpio_id: i32 = req.0;
42    let path_gpio_level = req.1.clone();
43    let path_gpio_level_copy = req.1.clone(); // TODO - OMG this is horrible
44    let gpio_arc_mutex = state.gpio_arc_mutex.clone();
45
46    // https://github.com/actix/examples/blob/master/async_db/src/main.rs
47    // https://github.com/actix/examples/blob/master/actix_todo/src/api.rs
48    // https://stackoverflow.com/questions/54164682/
49    state
50        .db
51        .send(CheckGpioLevel {
52            gpio_id: path_gpio_id,
53            gpio_level: (&path_gpio_level).to_string(),
54        })
55        .from_err()
56        .and_then(|res| future::result(res).from_err())
57        .and_then(move |_| {
58            // Update GPIO level on RPi
59            let level_updated =
60                rpi::set_gpio_level_rpi(path_gpio_id, &path_gpio_level, gpio_arc_mutex);
61            future::result(level_updated).from_err()
62        })
63        .and_then(move |_| {
64            // Update database to correspond with above
65            state
66                .db
67                .send(SetGpioLevel {
68                    gpio_id: path_gpio_id,
69                    gpio_level: (&path_gpio_level_copy).to_string(),
70                })
71                .from_err()
72        })
73        .and_then(|res| future::result(res).from_err())
74        .then(|res: Result<models::Gpio, actixError>| match res {
75            Ok(response) => Ok(HttpResponse::Ok().json(response)),
76            Err(err) => {
77                let err_string = err.to_string();
78                let mut response = HttpResponse::from_error(err);
79                response.set_body(err_string);
80                Ok(response)
81            }
82        })
83        .responder()
84}
85
86/// creates and returns the app after mounting all routes/resources
87pub fn create_app(app_state: AppState) -> App<AppState> {
88    App::with_state(app_state)
89        // enable logger
90        .middleware(middleware::Logger::default())
91        .resource("/status/{id}", |r| {
92            r.method(http::Method::GET).with(gpio_status_route)
93        })
94        .resource("/set/level/{id}/{level}", |r| {
95            r.method(http::Method::GET).with(set_gpio_level_route)
96        })
97}