worterbuch_common/
lib.rs

1/*
2 *  Worterbuch common modules library
3 *
4 *  Copyright (C) 2024 Michael Bachmann
5 *
6 *  This program is free software: you can redistribute it and/or modify
7 *  it under the terms of the GNU Affero General Public License as published by
8 *  the Free Software Foundation, either version 3 of the License, or
9 *  (at your option) any later version.
10 *
11 *  This program is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *  GNU Affero General Public License for more details.
15 *
16 *  You should have received a copy of the GNU Affero General Public License
17 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
18 */
19
20pub mod benchmark;
21mod client;
22pub mod error;
23mod server;
24pub mod tcp;
25
26pub use client::*;
27use serde_json::json;
28pub use server::*;
29
30use error::WorterbuchResult;
31use serde::{de::DeserializeOwned, Deserialize, Serialize};
32use serde_repr::*;
33use sha2::{Digest, Sha256};
34use std::{fmt, ops::Deref};
35
36pub const PROTOCOL_VERSION: &str = "0.11";
37
38pub const SYSTEM_TOPIC_ROOT: &str = "$SYS";
39pub const SYSTEM_TOPIC_ROOT_PREFIX: &str = "$SYS/";
40pub const SYSTEM_TOPIC_CLIENTS: &str = "clients";
41pub const SYSTEM_TOPIC_VERSION: &str = "version";
42pub const SYSTEM_TOPIC_LICENSE: &str = "license";
43pub const SYSTEM_TOPIC_SOURCES: &str = "source-code";
44pub const SYSTEM_TOPIC_SUBSCRIPTIONS: &str = "subscriptions";
45pub const SYSTEM_TOPIC_CLIENTS_PROTOCOL: &str = "protocol";
46pub const SYSTEM_TOPIC_CLIENTS_ADDRESS: &str = "address";
47pub const SYSTEM_TOPIC_LAST_WILL: &str = "lastWill";
48pub const SYSTEM_TOPIC_GRAVE_GOODS: &str = "graveGoods";
49pub const SYSTEM_TOPIC_CLIENT_NAME: &str = "clientName";
50pub const SYSTEM_TOPIC_SUPPORTED_PROTOCOL_VERSION: &str = "protocolVersion";
51
52pub type TransactionId = u64;
53pub type RequestPattern = String;
54pub type RequestPatterns = Vec<RequestPattern>;
55pub type Key = String;
56pub type Value = serde_json::Value;
57pub type KeyValuePairs = Vec<KeyValuePair>;
58pub type TypedKeyValuePairs<T> = Vec<TypedKeyValuePair<T>>;
59pub type MetaData = String;
60pub type Path = String;
61pub type ProtocolVersionSegment = u16;
62pub type ProtocolVersions = Vec<ProtocolVersion>;
63pub type LastWill = KeyValuePairs;
64pub type GraveGoods = RequestPatterns;
65pub type UniqueFlag = bool;
66pub type LiveOnlyFlag = bool;
67pub type AuthToken = String;
68
69#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
70#[serde(rename_all = "camelCase")]
71pub enum Privilege {
72    Read,
73    Write,
74    Delete,
75}
76
77impl fmt::Display for Privilege {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        match self {
80            Privilege::Read => "read".fmt(f),
81            Privilege::Write => "write".fmt(f),
82            Privilege::Delete => "delete".fmt(f),
83        }
84    }
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)]
88#[repr(u8)]
89pub enum ErrorCode {
90    IllegalWildcard = 0b00000000,
91    IllegalMultiWildcard = 0b00000001,
92    MultiWildcardAtIllegalPosition = 0b00000010,
93    IoError = 0b00000011,
94    SerdeError = 0b00000100,
95    NoSuchValue = 0b00000101,
96    NotSubscribed = 0b00000110,
97    ProtocolNegotiationFailed = 0b00000111,
98    InvalidServerResponse = 0b00001000,
99    ReadOnlyKey = 0b00001001,
100    AuthorizationFailed = 0b00001010,
101    AuthorizationRequired = 0b00001011,
102    AlreadyAuthorized = 0b00001100,
103    MissingValue = 0b00001101,
104    Unauthorized = 0b00001110,
105    NoPubStream = 0b00001111,
106    Other = 0b11111111,
107}
108
109impl fmt::Display for ErrorCode {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        (self.to_owned() as u8).fmt(f)
112    }
113}
114
115#[macro_export]
116macro_rules! topic {
117    ($( $x:expr ),+ ) => {
118        {
119            let mut segments = Vec::new();
120            $(
121                segments.push($x.to_string());
122            )+
123            segments.join("/")
124        }
125    };
126}
127
128pub type Version = String;
129pub type ProtocolVersion = String;
130
131#[derive(Debug, Clone, PartialEq, Eq, Serialize, Hash, Deserialize)]
132pub enum Protocol {
133    TCP,
134    WS,
135    HTTP,
136    UNIX,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
140#[serde(rename_all = "camelCase")]
141pub struct KeyValuePair {
142    pub key: Key,
143    pub value: Value,
144}
145
146impl fmt::Display for KeyValuePair {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        write!(f, "{}={}", self.key, self.value)
149    }
150}
151
152impl From<KeyValuePair> for Option<Value> {
153    fn from(kvp: KeyValuePair) -> Self {
154        Some(kvp.value)
155    }
156}
157
158impl From<KeyValuePair> for Value {
159    fn from(kvp: KeyValuePair) -> Self {
160        kvp.value
161    }
162}
163
164impl KeyValuePair {
165    pub fn new<S: Serialize>(key: String, value: S) -> Self {
166        (key, value).into()
167    }
168
169    pub fn of<S: Serialize>(key: String, value: S) -> Self {
170        KeyValuePair::new(key, value)
171    }
172}
173
174#[derive(Debug, Clone, PartialEq, Eq)]
175pub struct TypedKeyValuePair<T: DeserializeOwned> {
176    pub key: Key,
177    pub value: T,
178}
179
180impl<T: DeserializeOwned> TryFrom<KeyValuePair> for TypedKeyValuePair<T> {
181    type Error = serde_json::Error;
182
183    fn try_from(kvp: KeyValuePair) -> Result<Self, Self::Error> {
184        let deserialized = serde_json::from_value(kvp.value)?;
185        Ok(TypedKeyValuePair {
186            key: kvp.key,
187            value: deserialized,
188        })
189    }
190}
191
192impl<S: Serialize> From<(String, S)> for KeyValuePair {
193    fn from((key, value): (String, S)) -> Self {
194        let value = json!(value);
195        KeyValuePair { key, value }
196    }
197}
198
199impl<S: Serialize> From<(&str, S)> for KeyValuePair {
200    fn from((key, value): (&str, S)) -> Self {
201        let value = json!(value);
202        KeyValuePair {
203            key: key.to_owned(),
204            value,
205        }
206    }
207}
208
209// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord, Tags)]
210pub type RegularKeySegment = String;
211
212pub fn parse_segments(pattern: &str) -> WorterbuchResult<Vec<RegularKeySegment>> {
213    let mut segments = Vec::new();
214    for segment in pattern.split('/') {
215        let ks: KeySegment = segment.into();
216        match ks {
217            KeySegment::Regular(reg) => segments.push(reg),
218            KeySegment::Wildcard => {
219                return Err(error::WorterbuchError::IllegalWildcard(pattern.to_owned()))
220            }
221            KeySegment::MultiWildcard => {
222                return Err(error::WorterbuchError::IllegalMultiWildcard(
223                    pattern.to_owned(),
224                ))
225            }
226        }
227    }
228    Ok(segments)
229}
230
231#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
232pub enum KeySegment {
233    Regular(RegularKeySegment),
234    Wildcard,
235    MultiWildcard,
236    // RegexWildcard(String),
237}
238
239pub fn format_path(path: &[KeySegment]) -> String {
240    path.iter()
241        .map(|seg| format!("{seg}"))
242        .collect::<Vec<String>>()
243        .join("/")
244}
245
246impl From<RegularKeySegment> for KeySegment {
247    fn from(reg: RegularKeySegment) -> Self {
248        Self::Regular(reg)
249    }
250}
251
252impl Deref for KeySegment {
253    type Target = str;
254
255    fn deref(&self) -> &Self::Target {
256        match self {
257            KeySegment::Regular(reg) => reg,
258            KeySegment::Wildcard => "?",
259            KeySegment::MultiWildcard => "#",
260        }
261    }
262}
263
264impl fmt::Display for KeySegment {
265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266        match self {
267            KeySegment::Regular(segment) => segment.fmt(f),
268            KeySegment::Wildcard => write!(f, "?"),
269            KeySegment::MultiWildcard => write!(f, "#"),
270            // PathSegment::RegexWildcard(regex) => write!(f, "?{regex}?"),
271        }
272    }
273}
274
275impl From<&str> for KeySegment {
276    fn from(str: &str) -> Self {
277        match str {
278            "?" => KeySegment::Wildcard,
279            "#" => KeySegment::MultiWildcard,
280            other => KeySegment::Regular(other.to_owned()),
281        }
282    }
283}
284
285impl KeySegment {
286    pub fn parse(pattern: impl AsRef<str>) -> Vec<KeySegment> {
287        let segments = pattern.as_ref().split('/');
288        segments.map(KeySegment::from).collect()
289    }
290}
291
292pub fn quote(str: impl AsRef<str>) -> String {
293    let str_ref = str.as_ref();
294    if str_ref.starts_with('\"') && str_ref.ends_with('\"') {
295        str_ref.to_owned()
296    } else {
297        format!("\"{str_ref}\"")
298    }
299}
300
301pub fn digest_token(auth_token: &Option<String>, client_id: String) -> Option<String> {
302    auth_token.as_deref().map(|token| {
303        let salted = client_id + token;
304        let mut hasher = Sha256::new();
305        hasher.update(salted.as_bytes());
306        format!("{:x}", hasher.finalize())
307    })
308}
309
310#[cfg(test)]
311mod test {
312    use crate::ErrorCode;
313    use std::cmp::Ordering;
314
315    #[test]
316    fn protocol_versions_are_sorted_correctly() {
317        assert_eq!("0.1".cmp("0.2"), Ordering::Less);
318        assert_eq!("0.9".cmp("1.0"), Ordering::Less);
319        assert_eq!("1.2".cmp("1.3"), Ordering::Less);
320        assert_eq!("1.234".cmp("1.3"), Ordering::Less);
321
322        assert_eq!("0.1".cmp("0.1"), Ordering::Equal);
323        assert_eq!("0.9".cmp("0.9"), Ordering::Equal);
324        assert_eq!("1.2".cmp("1.2"), Ordering::Equal);
325
326        assert_eq!("0.2".cmp("0.1"), Ordering::Greater);
327        assert_eq!("1.0".cmp("0.9"), Ordering::Greater);
328        assert_eq!("1.3".cmp("1.2"), Ordering::Greater);
329        assert_eq!("1.3".cmp("1.234"), Ordering::Greater);
330        assert_eq!("13.45".cmp("1.345"), Ordering::Greater);
331
332        assert_eq!("0.3", "0.3".min("0.5"));
333        assert_eq!("0.8", "0.8".min("1.2"));
334        assert_eq!("2.3", "2.3".min("3.1"));
335
336        assert_eq!("0.3", "0.5".min("0.3"));
337        assert_eq!("0.8", "1.2".min("0.8"));
338        assert_eq!("2.34", "3.1".min("2.34"));
339
340        let mut versions = vec!["1.2", "0.456", "9.0", "3.15"];
341        versions.sort();
342        assert_eq!(vec!["0.456", "1.2", "3.15", "9.0"], versions);
343    }
344
345    #[test]
346    fn topic_macro_generates_topic_correctly() {
347        assert_eq!(
348            "hello/world/foo/bar",
349            topic!("hello", "world", "foo", "bar")
350        );
351    }
352
353    #[test]
354    fn error_codes_are_serialized_as_numbers() {
355        assert_eq!(
356            "1",
357            serde_json::to_string(&ErrorCode::IllegalMultiWildcard).unwrap()
358        )
359    }
360
361    #[test]
362    fn error_codes_are_deserialized_from_numbers() {
363        assert_eq!(
364            ErrorCode::ProtocolNegotiationFailed,
365            serde_json::from_str("7").unwrap()
366        )
367    }
368}