1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
use super::{Value, ValueLike, ValueType};
use std::collections::HashSet;

/// Represents a definition of a field, which is comprised of its name, type
/// of value, and any associated attributes
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct FieldDefinition {
    pub(super) name: String,
    r#type: ValueType,
    attributes: Vec<FieldAttribute>,
}

impl FieldDefinition {
    /// Creates a new field definition with the given name and value and no attributes
    pub fn new<N: Into<String>, T: Into<ValueType>>(name: N, r#type: T) -> Self {
        Self::new_with_attributes(name, r#type, HashSet::new())
    }

    /// Creates a new field definition with the given name, value, and attributes
    pub fn new_with_attributes<
        N: Into<String>,
        T: Into<ValueType>,
        A: IntoIterator<Item = FieldAttribute>,
    >(
        name: N,
        r#type: T,
        attributes: A,
    ) -> Self {
        // Filter out duplicates of field attributes
        let mut attributes: Vec<FieldAttribute> = attributes.into_iter().collect();
        attributes.sort();
        attributes.dedup();

        Self {
            name: name.into(),
            r#type: r#type.into(),
            attributes,
        }
    }

    /// The name of the field tied to the definition
    #[inline]
    pub fn name(&self) -> &str {
        &self.name
    }

    /// The type of value of the field tied to the definition
    #[inline]
    pub fn r#type(&self) -> &ValueType {
        &self.r#type
    }

    /// The attributes associated with the field tied to the definition
    #[inline]
    pub fn attributes(&self) -> &[FieldAttribute] {
        &self.attributes
    }

    /// Returns true if this field is marked as indexed (used in databases)
    /// in its definition
    #[inline]
    pub fn is_indexed(&self) -> bool {
        self.attributes().contains(&FieldAttribute::Indexed)
    }

    /// Returns true if this field marked as immutable in its definition and
    /// should not be updated
    #[inline]
    pub fn is_immutable(&self) -> bool {
        self.attributes().contains(&FieldAttribute::Immutable)
    }

    /// Returns true if this field marked as computed
    #[inline]
    pub fn is_computed(&self) -> bool {
        self.attributes().contains(&FieldAttribute::Computed)
    }
}

impl From<Field> for FieldDefinition {
    fn from(field: Field) -> Self {
        Self::new_with_attributes(field.name, field.value, field.attributes)
    }
}

impl<'a> From<&'a Field> for FieldDefinition {
    fn from(field: &'a Field) -> Self {
        Self::new_with_attributes(field.name(), field.value(), field.attributes.clone())
    }
}

/// Represents a field contained within some ent
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct Field {
    name: String,
    value: Value,
    attributes: Vec<FieldAttribute>,
}

impl Field {
    /// Creates a new field with the given name and value and no attributes
    pub fn new<N: Into<String>, V: ValueLike>(name: N, value: V) -> Self {
        Self::new_with_attributes(name, value, HashSet::new())
    }

    /// Creates a new field with the given name, value, and attributes
    pub fn new_with_attributes<
        N: Into<String>,
        V: ValueLike,
        A: IntoIterator<Item = FieldAttribute>,
    >(
        name: N,
        value: V,
        attributes: A,
    ) -> Self {
        // Filter out duplicates of field attributes
        let mut attributes: Vec<FieldAttribute> = attributes.into_iter().collect();
        attributes.sort();
        attributes.dedup();

        Self {
            name: name.into(),
            value: value.into_value(),
            attributes,
        }
    }

    /// The name of the field
    #[inline]
    pub fn name(&self) -> &str {
        &self.name
    }

    /// The value of the field
    #[inline]
    pub fn value(&self) -> &Value {
        &self.value
    }

    /// Mutable value of the field
    #[inline]
    pub fn value_mut(&mut self) -> &mut Value {
        &mut self.value
    }

    /// Converts field into its value
    #[inline]
    pub fn into_value(self) -> Value {
        self.value
    }

    /// The type of the value associated with the field
    pub fn to_value_type(&self) -> ValueType {
        self.value.to_type()
    }

    /// The attributes associated with the field
    #[inline]
    pub fn attributes(&self) -> &[FieldAttribute] {
        &self.attributes
    }

    /// Returns true if this field is marked as indexed (used in databases)
    #[inline]
    pub fn is_indexed(&self) -> bool {
        self.attributes().contains(&FieldAttribute::Indexed)
    }

    /// Returns true if this field marked as immutable and should not be updated
    #[inline]
    pub fn is_immutable(&self) -> bool {
        self.attributes().contains(&FieldAttribute::Immutable)
    }

    /// Returns true if this field marked as computed
    #[inline]
    pub fn is_computed(&self) -> bool {
        self.attributes().contains(&FieldAttribute::Computed)
    }
}

/// Represents an attribute associated with a field for an ent
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "async-graphql", derive(async_graphql::Enum))]
pub enum FieldAttribute {
    /// Indicates that this field is indexed for faster lookup
    Indexed,

    /// Indicates that this field is immutable, meaning that it cannot be
    /// changed after being initialized
    Immutable,

    /// Indicates that this field is computed and does not have a standard
    /// data representation within the ent (although it may have a cached value)
    Computed,
}