Skip to main content

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}