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    /// # async fn example() {
100    /// let props_data = json!({"timeout": "30", "retries": "3"});
101    /// let properties = Properties::from(props_data);
102    ///
103    /// let timeout: Option<u32> = properties.get_property("timeout").await;
104    /// let retries: Option<i32> = properties.get_property("retries").await;
105    /// # }
106    /// ```
107    pub async 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    /// ```ignore
134    /// use serde_json::json;
135    /// use apollo_client::namespace::properties::Properties;
136    ///
137    /// # async fn example() {
138    /// let props_data = json!({"app.name": "MyApp"});
139    /// let properties = Properties::from(props_data);
140    ///
141    /// let app_name = properties.get_string("app.name").await;
142    /// assert_eq!(app_name, Some("MyApp".to_string()));
143    /// # }
144    /// ```
145    pub async fn get_string(&self, key: &str) -> Option<String> {
146        self.get_property::<String>(key).await
147    }
148
149    /// Get a property from the cache as an integer.
150    ///
151    /// This method retrieves a property value and attempts to parse it as an `i64`.
152    /// It's a convenience method that wraps `get_property::<i64>()`.
153    ///
154    /// # Arguments
155    ///
156    /// * `key` - The key to get the property for.
157    ///
158    /// # Returns
159    ///
160    /// * `Some(i64)` - The property value as an integer if it exists and can be parsed
161    /// * `None` - If the key doesn't exist or the value cannot be parsed as an integer
162    ///
163    /// # Examples
164    ///
165    /// ```ignore
166    /// use serde_json::json;
167    /// use apollo_client::namespace::properties::Properties;
168    ///
169    /// # async fn example() {
170    /// let props_data = json!({"server.port": "8080"});
171    /// let properties = Properties::from(props_data);
172    ///
173    /// let port = properties.get_int("server.port").await;
174    /// assert_eq!(port, Some(8080));
175    /// # }
176    /// ```
177    pub async fn get_int(&self, key: &str) -> Option<i64> {
178        self.get_property::<i64>(key).await
179    }
180
181    /// Get a property from the cache as a float.
182    ///
183    /// This method retrieves a property value and attempts to parse it as an `f64`.
184    /// It's a convenience method that wraps `get_property::<f64>()`.
185    ///
186    /// # Arguments
187    ///
188    /// * `key` - The key to get the property for.
189    ///
190    /// # Returns
191    ///
192    /// * `Some(f64)` - The property value as a float if it exists and can be parsed
193    /// * `None` - If the key doesn't exist or the value cannot be parsed as a float
194    ///
195    /// # Examples
196    ///
197    /// ```ignore
198    /// use serde_json::json;
199    /// use apollo_client::namespace::properties::Properties;
200    ///
201    /// # async fn example() {
202    /// let props_data = json!({"timeout.seconds": "30.5"});
203    /// let properties = Properties::from(props_data);
204    ///
205    /// let timeout = properties.get_float("timeout.seconds").await;
206    /// assert_eq!(timeout, Some(30.5));
207    /// # }
208    /// ```
209    pub async fn get_float(&self, key: &str) -> Option<f64> {
210        self.get_property::<f64>(key).await
211    }
212
213    /// Get a property from the cache as a boolean.
214    ///
215    /// This method retrieves a property value and attempts to parse it as a `bool`.
216    /// It's a convenience method that wraps `get_property::<bool>()`. The parsing
217    /// follows Rust's standard boolean parsing rules (accepts "true"/"false").
218    ///
219    /// # Arguments
220    ///
221    /// * `key` - The key to get the property for.
222    ///
223    /// # Returns
224    ///
225    /// * `Some(bool)` - The property value as a boolean if it exists and can be parsed
226    /// * `None` - If the key doesn't exist or the value cannot be parsed as a boolean
227    ///
228    /// # Examples
229    ///
230    /// ```ignore
231    /// use serde_json::json;
232    /// use apollo_client::namespace::properties::Properties;
233    ///
234    /// # async fn example() {
235    /// let props_data = json!({"debug.enabled": "true"});
236    /// let properties = Properties::from(props_data);
237    ///
238    /// let debug_enabled = properties.get_bool("debug.enabled").await;
239    /// assert_eq!(debug_enabled, Some(true));
240    /// # }
241    /// ```
242    pub async fn get_bool(&self, key: &str) -> Option<bool> {
243        self.get_property::<bool>(key).await
244    }
245}
246
247/// Converts a `serde_json::Value` into a `Properties` instance.
248///
249/// This implementation allows for easy creation of `Properties` instances from
250/// raw JSON data, typically used by the namespace detection system. The JSON
251/// value is expected to be an object where keys are property names and values
252/// are the property values (typically strings).
253///
254/// # Examples
255///
256/// ```ignore
257/// use serde_json::json;
258/// use apollo_client::namespace::properties::Properties;
259///
260/// let props_data = json!({
261///     "app.name": "MyApp",
262///     "app.version": "1.0.0"
263/// });
264///
265/// let properties = Properties::from(props_data);
266/// ```
267impl From<serde_json::Value> for Properties {
268    fn from(value: serde_json::Value) -> Self {
269        Self { value }
270    }
271}