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