raspberry_web/
utilities.rs

1use crate::errors::RpWebError;
2use crate::models;
3use crate::schema::gpio_state::dsl::*;
4use chrono::Local;
5use diesel::prelude::*;
6use std::collections::HashMap;
7use std::u8::{MAX, MIN};
8
9pub fn reset_table_gpio_state(connection: &SqliteConnection) -> Result<(), RpWebError> {
10    info!("Resetting all fields in table 'gpio_state'...");
11
12    // Get all GPIO id's from table gpio_state
13    use crate::schema::gpio_state::dsl::*;
14    let gpio_ids_db: Vec<i32> = gpio_state
15        .load::<models::Gpio>(connection)?
16        .into_iter()
17        .map(|element| element.gpio_id)
18        .collect();
19
20    for idx in gpio_ids_db.iter() {
21        let target = gpio_state.filter(gpio_id.eq(idx));
22
23        let n_updated = diesel::update(target)
24            .set((
25                in_use.eq(0),
26                last_change.eq(Local::now().naive_local().to_string()),
27                // These two next ones can be discussed
28                gpio_mode.eq(""),
29                gpio_level.eq(""),
30            ))
31            .execute(connection)?; // DatabaseError
32
33        if n_updated == 1 {
34            debug!("Reset values for GPIO #{}", idx);
35        } else {
36            let errs = format!(
37                "SQL for resetting table 'gpio_state' for GPIO #{} affects {} rows",
38                idx, n_updated
39            );
40            error!("{}", errs);
41            Err(RpWebError::new(&errs))?
42        }
43    }
44    Ok(())
45}
46
47pub fn get_allowed_states(
48    connection: &SqliteConnection, desired_type: &str,
49) -> Result<HashMap<&'static str, bool>, RpWebError> {
50    use crate::schema::allowed_states::dsl::*;
51
52    let res = allowed_states
53        .filter(state_type.eq(desired_type.to_lowercase()))
54        .load::<models::AllowedStates>(connection)? // DatabaseError
55        .pop()
56        .ok_or({ RpWebError::new("state_type not found") })? //Empty vector
57        .to_hashmap();
58
59    Ok(res)
60}
61
62pub fn set_gpio_in_use_db(id: i32, state: i32, conn: &SqliteConnection) -> Result<(), RpWebError> {
63    let target = gpio_state.filter(gpio_id.eq(id));
64
65    let result = diesel::update(target)
66        .set((
67            in_use.eq(state),
68            last_change.eq(Local::now().naive_local().to_string()),
69        ))
70        .execute(conn);
71
72    match result {
73        Ok(val) => {
74            if val == 1 {
75                info!("Set 'in_use={}' for GPIO #{}", state, id);
76            } else {
77                let errs = format!(
78                    "SQL statement 'in_use={}' for GPIO #{} affects {} rows",
79                    state, id, val
80                );
81                Err(RpWebError::new(&errs))?;
82            }
83        }
84        Err(err) => {
85            error!(
86                "Failed to update 'in_use={}' for GPIO #{}: {:?}",
87                state, id, err
88            );
89            Err(err)?;
90        }
91    }
92    Ok(())
93}
94
95pub fn set_gpio_mode_db(id: i32, mode: &str, conn: &SqliteConnection) -> Result<(), RpWebError> {
96    let target = gpio_state.filter(gpio_id.eq(id));
97
98    let result = diesel::update(target)
99        .set((
100            gpio_mode.eq(mode),
101            last_change.eq(Local::now().naive_local().to_string()),
102        ))
103        .execute(conn);
104
105    match result {
106        Ok(val) => {
107            if val == 1 {
108                info!("Set 'gpio_mode={}' for GPIO #{}", mode, id);
109            } else {
110                let errs = format!(
111                    "SQL statement 'gpio_mode={}' for GPIO #{} affects {} rows",
112                    mode, id, val
113                );
114                Err(RpWebError::new(&errs))?;
115            }
116        }
117        Err(err) => {
118            error!(
119                "Failed to update 'gpio_mode={}' for GPIO #{}: {:?}",
120                mode, id, err
121            );
122            Err(err)?;
123        }
124    }
125    Ok(())
126}
127
128pub fn set_gpio_level_db(id: i32, level: &str, conn: &SqliteConnection) -> Result<(), RpWebError> {
129    let target = gpio_state.filter(gpio_id.eq(id));
130
131    let result = diesel::update(target)
132        .set((
133            gpio_level.eq(level),
134            last_change.eq(Local::now().naive_local().to_string()),
135        ))
136        .execute(conn);
137
138    match result {
139        Ok(val) => {
140            if val == 1 {
141                info!("Set 'gpio_level={}' for GPIO #{}", level, id);
142            } else {
143                let errs = format!(
144                    "SQL statement 'gpio_level={}' for GPIO #{} affects {} rows",
145                    level, id, val
146                );
147                Err(RpWebError::new(&errs))?;
148            }
149        }
150        Err(err) => {
151            error!(
152                "Failed to update 'gpio_level={}' for GPIO #{}: {:?}",
153                level, id, err
154            );
155            Err(err)?;
156        }
157    }
158    Ok(())
159}
160
161/// Convert x: i32 to u8 if MIN(u8)=0 x <= x <= MAX(u8)=255
162pub fn i32_to_u8(x: i32) -> Result<u8, RpWebError> {
163    if MIN as i32 <= x && x <= MAX as i32 {
164        Ok(x as u8)
165    } else {
166        let errs = format!("Not satisfied: {} <= {} <= {}", MIN, x, MAX);
167        Err(RpWebError::new(&errs))
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use super::*;
174
175    #[test]
176    pub fn below_u8_min_must_fail() {
177        assert!(i32_to_u8(MIN as i32 - 1).is_err())
178    }
179
180    #[test]
181    pub fn above_u8_max_must_fail() {
182        assert!(i32_to_u8(MAX as i32 + 1).is_err())
183    }
184
185    #[test]
186    pub fn within_range_must_succeed() {
187        let xi32: i32 = 17;
188        let resu8 = i32_to_u8(xi32).unwrap();
189        assert_eq!(xi32 as u8, resu8);
190    }
191}