apollo_rust_client/namespace/
properties.rs

1//! Properties namespace implementation for handling key-value configuration data.
2//!
3//! This module provides the `Properties` struct, which wraps a `serde_json::Value`,
4//! and provides methods for working with properties-style configuration data.
5//! Properties format is commonly used for application configuration where data
6//! is stored as simple key-value pairs.
7//!
8//! The `Properties` struct is typically created automatically by the namespace
9//! detection system when a namespace name has no file extension (defaulting to
10//! properties format), but can also be created directly from any `serde_json::Value`.
11//!
12//! The properties implementation supports automatic parsing of the following types:
13//! - `String` - Text values
14//! - `i64` - Integer values
15//! - `f64` - Floating-point values
16//! - `bool` - Boolean values
17//!
18//! # Example: `.properties` file format
19//!
20//! ```properties
21//! app.name = MyApplication
22//! app.version = 1.0.0
23//! app.port = 8080
24//! app.debug = true
25//! ```
26
27use log::debug;
28use wasm_bindgen::prelude::wasm_bindgen;
29
30/// A wrapper around `serde_json::Value` for properties-style configuration data.
31///
32/// This struct provides a type-safe interface for working with properties configuration
33/// data retrieved from Apollo. Properties are typically stored as key-value pairs where
34/// all values are strings that can be parsed into various types.
35///
36/// # Thread Safety
37///
38/// This struct is `Clone` and `Debug`, making it easy to work with in concurrent
39/// environments. The underlying `serde_json::Value` is also thread-safe.
40///
41/// # Examples
42///
43/// ```rust,ignore
44/// use serde_json::json;
45/// use apollo_client::namespace::properties::Properties;
46///
47/// let props_data = json!({
48///     "database.host": "localhost",
49///     "database.port": "5432",
50///     "database.ssl": "true"
51/// });
52///
53/// let properties = Properties::from(props_data);
54///
55/// let host = properties.get_string("database.host");
56/// assert_eq!(host, Some("localhost".to_string()));
57///
58/// let port = properties.get_int("database.port");
59/// assert_eq!(port, Some(5432));
60///
61/// let ssl = properties.get_bool("database.ssl");
62/// assert_eq!(ssl, Some(true));
63/// ```
64#[wasm_bindgen]
65#[derive(Clone, Debug)]
66pub struct Properties {
67    /// The underlying JSON value containing the properties data
68    value: serde_json::Value,
69}
70
71impl Properties {
72    /// Gets a property value and attempts to parse it into the specified type.
73    ///
74    /// This is a generic method that can parse string values into any type that
75    /// implements `FromStr`. It first retrieves the value as a string, then
76    /// attempts to parse it into the target type.
77    ///
78    /// # Type Parameters
79    ///
80    /// * `T` - The target type to parse into. Must implement `std::str::FromStr`.
81    ///
82    /// # Arguments
83    ///
84    /// * `key` - The property key to retrieve
85    ///
86    /// # Returns
87    ///
88    /// * `Some(T)` - The parsed value if the key exists and parsing succeeds
89    /// * `None` - If the key doesn't exist, the value is not a string, or parsing fails
90    ///
91    /// # Examples
92    ///
93    /// ```rust,ignore
94    /// use serde_json::json;
95    /// use apollo_client::namespace::properties::Properties;
96    ///
97    /// let props_data = json!({"timeout": "30", "retries": "3"});
98    /// let properties = Properties::from(props_data);
99    ///
100    /// let timeout: Option<u32> = properties.get_property("timeout");
101    /// assert_eq!(timeout, Some(30));
102    ///
103    /// let retries: Option<i32> = properties.get_property("retries");
104    /// assert_eq!(retries, Some(3));
105    /// ```
106    #[must_use]
107    pub fn get_property<T: std::str::FromStr>(&self, key: &str) -> Option<T> {
108        debug!("Getting property for key {key}");
109
110        let value = self.value.get(key)?;
111        value.as_str().and_then(|s| s.parse::<T>().ok())
112    }
113}
114
115#[wasm_bindgen]
116impl Properties {
117    /// Get a property from the cache as a string.
118    ///
119    /// This method retrieves a property value and returns it as a `String`.
120    /// It's a convenience method that wraps `get_property::<String>()`.
121    ///
122    /// # Arguments
123    ///
124    /// * `key` - The key to get the property for.
125    ///
126    /// # Returns
127    ///
128    /// * `Some(String)` - The property value as a string if it exists
129    /// * `None` - If the key doesn't exist or the value cannot be converted to a string
130    ///
131    /// # Examples
132    ///
133    /// ```rust,ignore
134    /// use serde_json::json;
135    /// use apollo_client::namespace::properties::Properties;
136    ///
137    /// let props_data = json!({"app.name": "MyApp"});
138    /// let properties = Properties::from(props_data);
139    ///
140    /// let app_name = properties.get_string("app.name");
141    /// assert_eq!(app_name, Some("MyApp".to_string()));
142    /// ```
143    #[must_use]
144    pub fn get_string(&self, key: &str) -> Option<String> {
145        self.get_property::<String>(key)
146    }
147
148    /// Get a property from the cache as an integer.
149    ///
150    /// This method retrieves a property value and attempts to parse it as an `i64`.
151    /// It's a convenience method that wraps `get_property::<i64>()`.
152    ///
153    /// # Arguments
154    ///
155    /// * `key` - The key to get the property for.
156    ///
157    /// # Returns
158    ///
159    /// * `Some(i64)` - The property value as an integer if it exists and can be parsed
160    /// * `None` - If the key doesn't exist or the value cannot be parsed as an integer
161    ///
162    /// # Examples
163    ///
164    /// ```rust,ignore
165    /// use serde_json::json;
166    /// use apollo_client::namespace::properties::Properties;
167    ///
168    /// let props_data = json!({"server.port": "8080"});
169    /// let properties = Properties::from(props_data);
170    ///
171    /// let port = properties.get_int("server.port");
172    /// assert_eq!(port, Some(8080));
173    /// ```
174    #[must_use]
175    pub fn get_int(&self, key: &str) -> Option<i64> {
176        self.get_property::<i64>(key)
177    }
178
179    /// Get a property from the cache as a float.
180    ///
181    /// This method retrieves a property value and attempts to parse it as an `f64`.
182    /// It's a convenience method that wraps `get_property::<f64>()`.
183    ///
184    /// # Arguments
185    ///
186    /// * `key` - The key to get the property for.
187    ///
188    /// # Returns
189    ///
190    /// * `Some(f64)` - The property value as a float if it exists and can be parsed
191    /// * `None` - If the key doesn't exist or the value cannot be parsed as a float
192    ///
193    /// # Examples
194    ///
195    /// ```rust,ignore
196    /// use serde_json::json;
197    /// use apollo_client::namespace::properties::Properties;
198    ///
199    /// let props_data = json!({"timeout.seconds": "30.5"});
200    /// let properties = Properties::from(props_data);
201    ///
202    /// let timeout = properties.get_float("timeout.seconds");
203    /// assert_eq!(timeout, Some(30.5));
204    /// ```
205    #[must_use]
206    pub fn get_float(&self, key: &str) -> Option<f64> {
207        self.get_property::<f64>(key)
208    }
209
210    /// Get a property from the cache as a boolean.
211    ///
212    /// This method retrieves a property value and attempts to parse it as a `bool`.
213    /// It's a convenience method that wraps `get_property::<bool>()`. The parsing
214    /// follows Rust's standard boolean parsing rules (accepts "true"/"false").
215    ///
216    /// # Arguments
217    ///
218    /// * `key` - The key to get the property for.
219    ///
220    /// # Returns
221    ///
222    /// * `Some(bool)` - The property value as a boolean if it exists and can be parsed
223    /// * `None` - If the key doesn't exist or the value cannot be parsed as a boolean
224    ///
225    /// # Examples
226    ///
227    /// ```rust,ignore
228    /// use serde_json::json;
229    /// use apollo_client::namespace::properties::Properties;
230    ///
231    /// let props_data = json!({"debug.enabled": "true"});
232    /// let properties = Properties::from(props_data);
233    ///
234    /// let debug_enabled = properties.get_bool("debug.enabled");
235    /// assert_eq!(debug_enabled, Some(true));
236    /// ```
237    #[must_use]
238    pub fn get_bool(&self, key: &str) -> Option<bool> {
239        self.get_property::<bool>(key)
240    }
241}
242
243/// Converts a `serde_json::Value` into a `Properties` instance.
244///
245/// This implementation allows for easy creation of `Properties` instances from
246/// raw JSON data, typically used by the namespace detection system. The JSON
247/// value is expected to be an object where keys are property names and values
248/// are the property values (typically strings).
249///
250/// # Examples
251///
252/// ```rust,ignore
253/// use serde_json::json;
254/// use apollo_client::namespace::properties::Properties;
255///
256/// let props_data = json!({
257///     "app.name": "MyApp",
258///     "app.version": "1.0.0"
259/// });
260///
261/// let properties = Properties::from(props_data);
262///
263/// let app_name = properties.get_string("app.name");
264/// assert_eq!(app_name, Some("MyApp".to_string()));
265///
266/// let version = properties.get_string("app.version");
267/// assert_eq!(version, Some("1.0.0".to_string()));
268/// ```
269impl From<serde_json::Value> for Properties {
270    fn from(value: serde_json::Value) -> Self {
271        Self { value }
272    }
273}