rsiot/components_config/http_server/
get_endpoint.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Попытка переделать HTTP сервер
//!
//! Структура храняния данных точки GET

use std::{collections::HashMap, fmt::Debug};

use serde::{de::DeserializeOwned, Serialize};
use serde_json::to_string;

use crate::message::{Message, MsgDataBound};

/// Конфигурация отдельной точки GET
#[derive(Clone, Debug)]
pub struct GetEndpointConfig<TMsg, TData> {
    /// Путь
    ///
    /// Примеры:
    ///
    /// ```rust
    /// path: `/data`
    /// ```
    pub path: &'static str,

    /// Данные для точки GET
    ///
    /// На входной структуре необходимо реализовать:
    ///
    /// ```rust
    /// #[derive(Clone, Debug, Default, Deserialize, Serialize)]
    /// ```
    pub data: TData,

    /// Функция обновления данных на основе входящих сообщений
    pub fn_input: fn(&Message<TMsg>, &mut TData),
}

impl<TMsg, TData> GetEndpoint<TMsg> for GetEndpointConfig<TMsg, TData>
where
    TMsg: 'static + MsgDataBound,
    TData: 'static + Clone + Debug + DeserializeOwned + Serialize + Send + Sync,
{
    fn get_path(&self) -> &str {
        self.path
    }

    fn fn_input(&mut self, msg: &Message<TMsg>) {
        (self.fn_input)(msg, &mut self.data)
    }

    fn get_json_data(&self) -> Result<String, serde_json::Error> {
        to_string(&self.data)
    }

    fn clone_dyn(&self) -> Box<dyn GetEndpoint<TMsg>> {
        Box::new(self.clone())
    }
}

/// Трейт для обеспечения логики работы отдельной точик GET
///
/// В разных точках хранят данные в разных структурах (поле `data`). Трейт нужен для обработки в
/// массиве
pub trait GetEndpoint<TMsg>
where
    Self: Debug + Send + Sync,
{
    /// Получить путь для роутера
    fn get_path(&self) -> &str;

    /// Получить сохраненные данные в формате JSON
    fn get_json_data(&self) -> Result<String, serde_json::Error>;

    /// Обновление данных на основе входящих сообщений
    fn fn_input(&mut self, msg: &Message<TMsg>);

    /// Поддержка клонирования
    fn clone_dyn(&self) -> Box<dyn GetEndpoint<TMsg>>;
}

impl<TMsg> Clone for Box<dyn GetEndpoint<TMsg>> {
    fn clone(&self) -> Self {
        self.clone_dyn()
    }
}

/// Создать коллекцию точек GET на основе конфигурации
pub fn create_get_endpoints_hashmap<TMsg>(
    config_endpoints: &[Box<dyn GetEndpoint<TMsg>>],
) -> HashMap<String, Box<dyn GetEndpoint<TMsg>>>
where
    TMsg: MsgDataBound,
{
    let mut endpoints = HashMap::new();
    for endpoint in config_endpoints {
        endpoints.insert(endpoint.get_path().to_string(), endpoint.clone());
    }
    endpoints
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::collections::HashMap;

    use serde::{Deserialize, Serialize};

    use crate::message::example_message::Custom;

    #[test]
    fn test1() {
        let mut vec_trait: Vec<Box<dyn GetEndpoint<Custom>>> = vec![];

        #[derive(Clone, Debug, Deserialize, Serialize)]
        struct Data1 {}

        #[derive(Clone, Debug, Deserialize, Serialize)]
        struct Data2 {}

        let end1 = GetEndpointConfig {
            path: "/1",
            data: Data1 {},
            fn_input: |_, _| (),
        };
        let end2 = GetEndpointConfig {
            path: "/2",
            data: Data2 {},
            fn_input: |_, _| (),
        };
        vec_trait.push(Box::new(end1));
        vec_trait.push(Box::new(end2));

        // Собираем в словарь
        let mut map = HashMap::new();

        for e in vec_trait.into_iter() {
            let endpoint = e.get_path().to_string();
            map.insert(endpoint, e);
        }
    }
}