signald_rust/
signald.rs

1use crate::signaldrequest::SignaldRequestBuilder;
2use crate::signaldrequest::SignaldRequest;
3use std::time::{Instant, Duration};
4use bus::{BusReader};
5use std::sync::mpsc::RecvTimeoutError::Timeout;
6use std::sync::mpsc::RecvTimeoutError;
7use crate::signaldresponse::{SignaldResponse, ResponseType};
8use crate::signald::FilterType::{Id, Type};
9use crate::signaldresponse::ResponseType::BusUpdate;
10use crate::socket::Socket;
11use crate::socket::signaldsocket::SignaldSocket;
12
13pub static SOCKET_PATH: &'static str = "/var/run/signald/signald.sock";
14
15pub enum FilterType {
16    Id(String),
17    Type(ResponseType)
18}
19
20pub struct Signald {
21    // The signald socket
22    // socket: Box<dyn Socket + Send + Sync>,
23    socket: SignaldSocket,
24    // A count of all the sent messages on this socket
25    message_count: u32,
26}
27impl Signald {
28
29    /// Connect the default Signald socket
30    pub fn connect() -> Signald {
31        Signald::connect_path(&SOCKET_PATH)
32    }
33    /// Connect to a custom Signald socket
34    pub fn connect_path(socket_path: &str) -> Self {
35        let socket: SignaldSocket = SignaldSocket::connect(socket_path.to_string(), 100);
36
37        Self {
38            // socket: Box::new(socket),
39            socket: socket,
40            message_count: 0,
41        }
42    }
43    /// Send a signald request on the socket
44    pub fn send_request(&mut self, request: &SignaldRequest) {
45        self.message_count += 1;
46        self.socket.send_request(&request);
47    }
48
49    // Signald messages
50    // Todo: add attachments, etc
51    /// Send a message to the socket
52    pub async fn send(&mut self, username: String, recipient_number: String, message_body: Option<String>) {
53        let mut request_builder = SignaldRequestBuilder::new();
54        request_builder.set_type("send".to_string());
55        request_builder.set_username(username);
56        request_builder.set_recipient_number(recipient_number);
57        if let Some(i) = message_body {
58            request_builder.set_message_body(i);
59        }
60
61        let request = request_builder.build();
62        self.send_request(&request);
63    }
64
65    /// Enable receiving user events such as received messages
66    pub async fn subscribe(&mut self, username: String) -> Result<SignaldResponse, RecvTimeoutError> {
67        let id = self.message_count.to_string();
68
69        let mut request_builder = SignaldRequestBuilder::new();
70        request_builder.set_type("subscribe".to_string());
71        request_builder.set_username(username);
72        request_builder.set_id(id.clone());
73        let request = request_builder.build();
74
75        self.send_request(&request);
76        self.wait_for_request(Id(id)).await
77    }
78    /// Disable receiving user events such as received messages
79    pub async fn unsubscribe(&mut self, username: String) -> Result<SignaldResponse, RecvTimeoutError> {
80        let id = self.message_count.to_string();
81
82        let mut request_builder = SignaldRequestBuilder::new();
83        request_builder.set_type("unsubscribe".to_string());
84        request_builder.set_username(username);
85        request_builder.set_id(id.clone());
86        let request = request_builder.build();
87
88        self.send_request(&request);
89        self.wait_for_request(Id(id)).await
90    }
91    /// Link an existing signal account
92    pub async fn link(&mut self) -> Result<SignaldResponse, RecvTimeoutError> {
93        let id = self.message_count.to_string();
94
95        let mut request_builder = SignaldRequestBuilder::new();
96        request_builder.set_type("link".to_string());
97        request_builder.set_id(id.clone());
98        let request = request_builder.build();
99
100        self.send_request(&request);
101        self.wait_for_request(Id(id)).await
102    }
103    /// Get the current signald version
104    pub async fn version(&mut self) -> Result<SignaldResponse, RecvTimeoutError> {
105        let id = self.message_count.to_string();
106
107        let mut request_builder = SignaldRequestBuilder::new();
108        request_builder.set_type("version".to_string());
109        request_builder.set_id(id.clone());
110        let request = request_builder.build();
111
112        self.send_request(&request);
113        self.wait_for_request(Type(ResponseType::Version(None))).await
114    }
115    /// Query all the user's contacts
116    pub async fn list_contacts(&mut self, username: String) -> Result<SignaldResponse, RecvTimeoutError> {
117        let id = self.message_count.to_string();
118
119        let mut request_builder = SignaldRequestBuilder::new();
120        request_builder.set_type("list_contacts".to_string());
121        request_builder.set_username(username);
122        request_builder.set_id(id.clone());
123        let request = request_builder.build();
124
125        self.send_request(&request);
126        self.wait_for_request(Id(id)).await
127    }
128    /// Send a contact sync request to the other devices on this account
129    pub fn sync_contacts(&mut self, username: String) {
130        let mut request_builder = SignaldRequestBuilder::new();
131        request_builder.set_type("sync_contacts".to_string());
132        request_builder.set_username(username);
133        request_builder.set_id(self.message_count.to_string());
134        let request = request_builder.build();
135
136        self.send_request(&request);
137    }
138    /// Get a response stream that returns every received message on the socket
139    pub fn get_rx(&mut self) -> BusReader<SignaldResponse> {
140        self.socket.get_rx()
141    }
142
143    /// Get a response from the bus with a matching id or type
144    /// Returns a RecvTimeoutError if the message took more than 3 seconds to return
145    async fn wait_for_request(&mut self, filter: FilterType) -> Result<SignaldResponse, RecvTimeoutError> {
146        // The max possible time to receive a message
147        let end = Instant::now() + Duration::from_millis(3000);
148        let mut rx = self.socket.get_rx();
149
150        let result = rx.iter()
151            // Stop the receiver once the time is over, this keeps updating thanks to the update messages in systemdsocket
152            .take_while(|_| Instant::now() < end )
153            .find(|response| {
154                // The systemdsocket sends an 'update' message each second, don't parse this
155                if let BusUpdate = response.data { return false; }
156
157                Signald::filter_request(&filter, &response)
158            });
159
160        // When no results are found within the time limit, an error is returned
161        match result {
162            Some(x) => {
163                Ok(x)
164            },
165            None => {
166                Err(Timeout)
167            }
168        }
169
170    }
171
172    fn filter_request(filter: &FilterType, message: &SignaldResponse) -> bool {
173        match filter {
174            // Filter on id
175            Id(req_id) => {
176                match &message.id {
177                    Some(s) => {
178                        return s == req_id.as_str();
179                    },
180                    None => {
181                        false
182                    }
183                }
184            }
185            // Filter on response type
186            Type(req_type) => {
187                let disc1 = std::mem::discriminant(req_type);
188                let disc2 = std::mem::discriminant(&message.data);
189                return disc1 == disc2;
190            }
191        }
192    }
193
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    #[test]
201    fn test_filter_request_id_success() {
202        let id = "test".to_string();
203
204        let message = SignaldResponse {
205            id: Some(id.clone()),
206            data: ResponseType::Subscribed
207        };
208
209        assert!(Signald::filter_request(&Id(id), &message));
210    }
211
212    #[test]
213    fn test_filter_request_id_wrong() {
214        let id = "test".to_string();
215
216        let message = SignaldResponse {
217            id: Some(id.clone()),
218            data: ResponseType::Subscribed
219        };
220
221        assert!(!Signald::filter_request(&Id("INCORRECT_ID".to_string()), &message));
222    }
223
224    #[test]
225    fn test_filter_request_type_correct() {
226        let message = SignaldResponse {
227            id: None,
228            data: ResponseType::Subscribed
229        };
230
231        assert!(Signald::filter_request(&Type(ResponseType::Subscribed), &message));
232    }
233
234    #[test]
235    fn test_filter_request_type_wrong() {
236        let message = SignaldResponse {
237            id: None,
238            data: ResponseType::Subscribed
239        };
240
241        assert!(!Signald::filter_request(&Type(ResponseType::Unsubscribed), &message));
242    }
243}