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}