drasi_source_application/property_builder.rs
1// Copyright 2025 The Drasi Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use drasi_core::models::{ElementPropertyMap, ElementValue};
16use std::sync::Arc;
17
18/// Builder for creating graph element property maps
19///
20/// `PropertyMapBuilder` provides a fluent API for constructing [`ElementPropertyMap`] instances
21/// used when inserting or updating nodes and relationships in the graph.
22///
23/// # Supported Property Types
24///
25/// - **String**: Text values
26/// - **Integer**: 64-bit signed integers
27/// - **Float**: 64-bit floating point numbers
28/// - **Boolean**: true/false values
29/// - **Null**: Explicit null values
30///
31/// # Thread Safety
32///
33/// `PropertyMapBuilder` is not thread-safe. Create a new builder for each thread.
34///
35/// # Examples
36///
37/// ## Basic Usage
38///
39/// ```
40/// use drasi_source_application::PropertyMapBuilder;
41///
42/// let properties = PropertyMapBuilder::new()
43/// .with_string("name", "Alice")
44/// .with_integer("age", 30)
45/// .with_bool("active", true)
46/// .with_float("score", 95.5)
47/// .build();
48/// ```
49///
50/// ## With Application Source
51///
52/// ```no_run
53/// use drasi_source_application::{ApplicationSource, ApplicationSourceConfig, PropertyMapBuilder};
54/// use std::collections::HashMap;
55///
56/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
57/// let config = ApplicationSourceConfig { properties: HashMap::new() };
58/// let (source, handle) = ApplicationSource::new("events", config)?;
59///
60/// // Create a user node with multiple properties
61/// let properties = PropertyMapBuilder::new()
62/// .with_string("username", "alice")
63/// .with_string("email", "alice@example.com")
64/// .with_integer("age", 30)
65/// .with_bool("verified", true)
66/// .with_float("rating", 4.8)
67/// .build();
68///
69/// handle.send_node_insert("user-1", vec!["User"], properties).await?;
70/// # Ok(())
71/// # }
72/// ```
73///
74/// ## Null Values
75///
76/// ```
77/// use drasi_source_application::PropertyMapBuilder;
78///
79/// let properties = PropertyMapBuilder::new()
80/// .with_string("name", "Bob")
81/// .with_null("middle_name") // Explicit null
82/// .build();
83/// ```
84pub struct PropertyMapBuilder {
85 properties: ElementPropertyMap,
86}
87
88impl PropertyMapBuilder {
89 /// Create a new empty property map builder
90 ///
91 /// # Examples
92 ///
93 /// ```
94 /// use drasi_source_application::PropertyMapBuilder;
95 ///
96 /// let builder = PropertyMapBuilder::new();
97 /// let properties = builder.build();
98 /// ```
99 pub fn new() -> Self {
100 Self {
101 properties: ElementPropertyMap::new(),
102 }
103 }
104
105 /// Add a string property
106 ///
107 /// # Arguments
108 ///
109 /// * `key` - Property name
110 /// * `value` - String value (converted to `Arc<str>` internally)
111 ///
112 /// # Examples
113 ///
114 /// ```
115 /// use drasi_source_application::PropertyMapBuilder;
116 ///
117 /// let properties = PropertyMapBuilder::new()
118 /// .with_string("name", "Alice")
119 /// .with_string("email", "alice@example.com")
120 /// .build();
121 /// ```
122 pub fn with_string(mut self, key: impl AsRef<str>, value: impl Into<String>) -> Self {
123 self.properties.insert(
124 key.as_ref(),
125 ElementValue::String(Arc::from(value.into().as_str())),
126 );
127 self
128 }
129
130 /// Add an integer property (64-bit signed)
131 ///
132 /// # Arguments
133 ///
134 /// * `key` - Property name
135 /// * `value` - Integer value (i64)
136 ///
137 /// # Examples
138 ///
139 /// ```
140 /// use drasi_source_application::PropertyMapBuilder;
141 ///
142 /// let properties = PropertyMapBuilder::new()
143 /// .with_integer("age", 30)
144 /// .with_integer("count", 1000)
145 /// .build();
146 /// ```
147 pub fn with_integer(mut self, key: impl AsRef<str>, value: i64) -> Self {
148 self.properties
149 .insert(key.as_ref(), ElementValue::Integer(value));
150 self
151 }
152
153 /// Add a floating-point property (64-bit)
154 ///
155 /// # Arguments
156 ///
157 /// * `key` - Property name
158 /// * `value` - Float value (f64)
159 ///
160 /// # Examples
161 ///
162 /// ```
163 /// use drasi_source_application::PropertyMapBuilder;
164 ///
165 /// let properties = PropertyMapBuilder::new()
166 /// .with_float("rating", 4.5)
167 /// .with_float("price", 29.99)
168 /// .build();
169 /// ```
170 pub fn with_float(mut self, key: impl AsRef<str>, value: f64) -> Self {
171 self.properties
172 .insert(key.as_ref(), ElementValue::Float(value.into()));
173 self
174 }
175
176 /// Add a boolean property
177 ///
178 /// # Arguments
179 ///
180 /// * `key` - Property name
181 /// * `value` - Boolean value
182 ///
183 /// # Examples
184 ///
185 /// ```
186 /// use drasi_source_application::PropertyMapBuilder;
187 ///
188 /// let properties = PropertyMapBuilder::new()
189 /// .with_bool("active", true)
190 /// .with_bool("verified", false)
191 /// .build();
192 /// ```
193 pub fn with_bool(mut self, key: impl AsRef<str>, value: bool) -> Self {
194 self.properties
195 .insert(key.as_ref(), ElementValue::Bool(value));
196 self
197 }
198
199 /// Add an explicit null property
200 ///
201 /// Use this to explicitly set a property to null, which is different from
202 /// not having the property at all.
203 ///
204 /// # Arguments
205 ///
206 /// * `key` - Property name
207 ///
208 /// # Examples
209 ///
210 /// ```
211 /// use drasi_source_application::PropertyMapBuilder;
212 ///
213 /// let properties = PropertyMapBuilder::new()
214 /// .with_string("first_name", "Alice")
215 /// .with_null("middle_name") // Explicitly null
216 /// .with_string("last_name", "Smith")
217 /// .build();
218 /// ```
219 pub fn with_null(mut self, key: impl AsRef<str>) -> Self {
220 self.properties.insert(key.as_ref(), ElementValue::Null);
221 self
222 }
223
224 /// Build the final property map
225 ///
226 /// Consumes the builder and returns the constructed [`ElementPropertyMap`].
227 ///
228 /// # Examples
229 ///
230 /// ```
231 /// use drasi_source_application::PropertyMapBuilder;
232 ///
233 /// let properties = PropertyMapBuilder::new()
234 /// .with_string("name", "Alice")
235 /// .with_integer("age", 30)
236 /// .build();
237 ///
238 /// // properties can now be used with send_node_insert, send_node_update, etc.
239 /// ```
240 pub fn build(self) -> ElementPropertyMap {
241 self.properties
242 }
243}
244
245impl Default for PropertyMapBuilder {
246 fn default() -> Self {
247 Self::new()
248 }
249}