opentalk_types_signaling/
module_data.rs

1// SPDX-FileCopyrightText: OpenTalk GmbH <mail@opentalk.eu>
2//
3// SPDX-License-Identifier: EUPL-1.2
4
5use std::collections::BTreeMap;
6
7use opentalk_types_common::{
8    modules::{ModuleId, module_id},
9    utils::ExampleData,
10};
11use serde::{Deserialize, Serialize};
12
13use crate::SignalingModuleFrontendData;
14
15/// A struct containing data for multiple signaling modules, each associated
16/// with the module's namespace.
17#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18#[serde(rename_all = "snake_case")]
19#[cfg_attr(
20    feature = "utoipa",
21    derive(utoipa::ToSchema),
22    schema(example = json!(ModuleData::example_data()))
23)]
24pub struct ModuleData(BTreeMap<ModuleId, serde_json::Value>);
25
26impl ModuleData {
27    /// Create a new empty [`ModuleData`].
28    pub fn new() -> Self {
29        Self(BTreeMap::default())
30    }
31
32    /// Get an iterator over all module data entries
33    pub fn iter(&self) -> impl Iterator<Item = (&ModuleId, &serde_json::Value)> {
34        self.0.iter()
35    }
36
37    /// Get the participant data for a specific module
38    pub fn get<T: SignalingModuleFrontendData>(&self) -> Result<Option<T>, serde_json::Error> {
39        if let Some(namespace) = T::NAMESPACE {
40            self.0
41                .get(&namespace)
42                .map(|m| serde_json::from_value(m.clone()))
43                .transpose()
44        } else {
45            Ok(None)
46        }
47    }
48
49    /// Set the participant data for a specific module
50    ///
51    /// If an entry with the namespace already exists, it will be overwritten.
52    /// If the namespace of `T` is [`None`], the data will not be stored at all.
53    pub fn insert<T: SignalingModuleFrontendData>(
54        &mut self,
55        data: &T,
56    ) -> Result<(), serde_json::Error> {
57        if let Some(namespace) = T::NAMESPACE {
58            let _ = self.0.insert(namespace, serde_json::to_value(data)?);
59        }
60        Ok(())
61    }
62
63    /// Query whether the module data contains a value for this key
64    pub fn contains_key(&self, key: &ModuleId) -> bool {
65        self.0.contains_key(key)
66    }
67
68    /// Remove an entry by the namespace of [`SignalingModuleFrontendData`] type.
69    ///
70    /// This method does not verify that the data actually can be deserialized
71    /// into the requested types, it just uses its namespace and removes an
72    /// entry with that namespace if it exists.
73    pub fn remove<T: SignalingModuleFrontendData>(&mut self) {
74        if let Some(namespace) = T::NAMESPACE {
75            self.remove_key(&namespace);
76        }
77    }
78
79    /// Remove an entry by its key if it exists.
80    pub fn remove_key(&mut self, key: &ModuleId) {
81        let _ = self.0.remove(key);
82    }
83
84    /// Take an entry by the namespace of a type.
85    ///
86    /// If the entry is present but can not be deserialized into the requested type,
87    /// the data will remain unchanged, the entry is not removed.
88    pub fn take<T: SignalingModuleFrontendData>(&mut self) -> Result<Option<T>, serde_json::Error> {
89        let entry = self.get::<T>()?;
90        self.remove::<T>();
91        Ok(entry)
92    }
93
94    /// Retains only the entries specified by the predicate
95    pub fn retain<F>(&mut self, f: F)
96    where
97        F: FnMut(&ModuleId, &mut serde_json::Value) -> bool,
98    {
99        self.0.retain(f);
100    }
101}
102
103impl ExampleData for ModuleData {
104    fn example_data() -> Self {
105        Self(BTreeMap::from([(
106            module_id!("livekit"),
107            serde_json::json!({
108                "public_url": "http://localhost:7880",
109                "service_url": "http://localhost:7880",
110                "api_key": "devkey",
111                "api_secret": "secret",
112            }),
113        )]))
114    }
115}