Skip to main content

powerplatform_dataverse_client/dataverse/
entity.rs

1use std::collections::HashMap;
2
3use chrono::{DateTime, Utc};
4use rust_decimal::Decimal;
5use serde::{Deserialize, Serialize};
6use uuid::Uuid;
7
8/// Represents a Dataverse attribute value.
9#[derive(Debug, Serialize, Deserialize, Clone)]
10#[serde(untagged)]
11pub enum Value {
12    /// Signed 64-bit integer.
13    Int(i64),
14    /// Floating point value.
15    Float(f64),
16    /// Exact decimal value.
17    Decimal(Decimal),
18    /// String value.
19    String(String),
20    /// Boolean value.
21    Boolean(bool),
22    /// Date/time value.
23    DateTime(DateTime<Utc>),
24    /// GUID value.
25    Guid(Uuid),
26    /// Money value.
27    Money(Money),
28    /// Single choice/status/state value.
29    OptionSetValue(OptionSetValue),
30    /// Multi-select choice value.
31    OptionSetValueCollection(OptionSetValueCollection),
32    /// Null value.
33    Null,
34    /// Entity reference value (lookup).
35    EntityReference(EntityReference),
36}
37
38/// Dataverse money value.
39#[derive(Debug, Serialize, Deserialize, Clone)]
40pub struct Money {
41    /// Monetary amount.
42    pub value: Decimal,
43}
44
45/// Dataverse single option-set value.
46#[derive(Debug, Serialize, Deserialize, Clone)]
47pub struct OptionSetValue {
48    /// Numeric option value.
49    pub value: i32,
50    /// Display label for the option, when provided.
51    pub name: Option<String>,
52}
53
54/// Dataverse multi-select option-set value.
55#[derive(Debug, Serialize, Deserialize, Clone)]
56pub struct OptionSetValueCollection {
57    /// Numeric option values.
58    pub values: Vec<i32>,
59}
60
61/// Reference to another Dataverse entity.
62#[derive(Debug, Serialize, Deserialize, Clone)]
63pub struct EntityReference {
64    /// Primary ID for the referenced entity.
65    pub id: Uuid,
66    /// Logical name for the referenced entity.
67    pub logical_name: String,
68    /// Primary name for the referenced entity, when provided.
69    pub name: Option<String>,
70}
71
72/// Attribute logical name.
73pub type Attribute = String;
74
75/// Dataverse entity record with attribute values.
76#[derive(Debug, Serialize, Deserialize, Clone)]
77pub struct Entity {
78    /// Primary ID for the entity record.
79    pub id: Uuid,
80    /// Logical name for the entity.
81    pub logical_name: String,
82    /// Primary name for the entity record, when provided.
83    pub name: Option<String>,
84    /// Attribute map keyed by logical names.
85    pub attributes: HashMap<Attribute, Value>,
86}
87
88impl Entity {
89    /// Create a new entity with the provided identity fields.
90    pub fn new(id: Uuid, logical_name: impl Into<String>, name: Option<String>) -> Self {
91        Self {
92            id,
93            logical_name: logical_name.into(),
94            name,
95            attributes: HashMap::new(),
96        }
97    }
98}
99
100impl Default for Entity {
101    fn default() -> Self {
102        Self {
103            id: Uuid::nil(),
104            logical_name: String::new(),
105            name: None,
106            attributes: HashMap::new(),
107        }
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::Entity;
114    use uuid::Uuid;
115
116    #[test]
117    fn new_entity_starts_with_empty_attribute_map() {
118        let id = Uuid::new_v4();
119        let entity = Entity::new(id, "account", Some("Acme".to_string()));
120
121        assert_eq!(entity.id, id);
122        assert_eq!(entity.logical_name, "account");
123        assert_eq!(entity.name.as_deref(), Some("Acme"));
124        assert!(entity.attributes.is_empty());
125    }
126
127    #[test]
128    fn default_entity_uses_nil_identity() {
129        let entity = Entity::default();
130
131        assert_eq!(entity.id, Uuid::nil());
132        assert!(entity.logical_name.is_empty());
133        assert!(entity.name.is_none());
134        assert!(entity.attributes.is_empty());
135    }
136}