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}