Skip to main content

zulip_api/message/
custom_profile.rs

1use serde::{Deserialize, Serialize};
2use serde_repr::{Deserialize_repr, Serialize_repr};
3
4use crate::{field_access, impl_cow, impl_default, impl_display};
5
6/// Represents the details of the field type with the field type as the name of the
7/// property itself. The current supported field types are as follows:
8///
9/// - `SHORT_TEXT`
10/// - `LONG_TEXT`
11/// - `DATE` for date-based fields.
12/// - `SELECT` for a list of options.
13/// - `URL` for links.
14/// - `EXTERNAL_ACCOUNT` for external accounts.
15/// - `USER` for selecting a user for the field.
16/// - `PRONOUNS` for a short text field with convenient typeahead for one's preferred pronouns.
17///
18/// **Changes**: `PRONOUNS` type added in Zulip 6.0 (feature level 151).
19#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
20pub struct CustomProfileFieldType {
21    id: CustomProfileFieldTypeId,
22    name: String,
23}
24
25impl CustomProfileFieldType {
26    /// Creates a new [CustomProfileFieldType].
27    pub const fn new() -> Self {
28        Self {
29            id: CustomProfileFieldTypeId::new(),
30            name: String::new(),
31        }
32    }
33}
34
35field_access! {
36    CustomProfileFieldType {
37        /// The ID of the custom profile field type.
38        id: CustomProfileFieldTypeId,
39    }
40}
41
42field_access! {
43    CustomProfileFieldType {
44        name: cow { str },
45    }
46}
47
48impl_cow!(CustomProfileFieldType);
49impl_default!(CustomProfileFieldType);
50impl_display!(CustomProfileFieldType);
51
52impl From<CustomProfileFieldTypeId> for CustomProfileFieldType {
53    fn from(val: CustomProfileFieldTypeId) -> Self {
54        Self {
55            id: val,
56            name: val.as_str().into(),
57        }
58    }
59}
60
61/// Represents the details of the field type with the field type as the name of the property itself.
62///
63/// The current supported field types are as follows:
64///
65/// - `SHORT_TEXT`
66/// - `LONG_TEXT`
67/// - `DATE` for date-based fields.
68/// - `SELECT` for a list of options.
69/// - `URL` for links.
70/// - `EXTERNAL_ACCOUNT` for external accounts.
71/// - `USER` for selecting a user for the field.
72/// - `PRONOUNS` for a short text field with convenient typeahead for one's preferred pronouns.
73///
74/// **Changes**: `PRONOUNS` type added in Zulip 6.0 (feature level 151).
75#[repr(u8)]
76#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize_repr, Serialize_repr)]
77pub enum CustomProfileFieldTypeId {
78    ShortText = 1,
79    LongText = 2,
80    Date = 3,
81    Select = 4,
82    Url = 5,
83    ExternalAccount = 6,
84    User = 7,
85    Pronouns = 8,
86}
87
88impl CustomProfileFieldTypeId {
89    /// Represents the short text field type.
90    pub const SHORT_TEXT: &str = "SHORT_TEXT";
91    /// Represents the long text field type.
92    pub const LONG_TEXT: &str = "LONG_TEXT";
93    /// Represents the date field type.
94    pub const DATE: &str = "DATE";
95    /// Represents the select field type.
96    pub const SELECT: &str = "SELECT";
97    /// Represents the url field type.
98    pub const URL: &str = "URL";
99    /// Represents the external account field type.
100    pub const EXTERNAL_ACCOUNT: &str = "EXTERNAL_ACCOUNT";
101    /// Represents the user field type.
102    pub const USER: &str = "USER";
103    /// Represents the pronouns field type.
104    pub const PRONOUNS: &str = "PRONOUNS";
105
106    /// Creates a new [CustomProfileFieldTypeId].
107    pub const fn new() -> Self {
108        Self::ShortText
109    }
110
111    /// Gets the [CustomProfileFieldTypeId] string representation.
112    pub const fn as_str(&self) -> &'static str {
113        match self {
114            Self::ShortText => Self::SHORT_TEXT,
115            Self::LongText => Self::LONG_TEXT,
116            Self::Date => Self::DATE,
117            Self::Select => Self::SELECT,
118            Self::Url => Self::URL,
119            Self::ExternalAccount => Self::EXTERNAL_ACCOUNT,
120            Self::User => Self::USER,
121            Self::Pronouns => Self::PRONOUNS,
122        }
123    }
124}
125
126impl_default!(CustomProfileFieldTypeId);
127impl_display!(CustomProfileFieldTypeId);
128
129/// Represents the details of a custom profile field configured for this organization.
130#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
131pub struct CustomProfileField {
132    id: u64,
133    #[serde(rename = "type")]
134    field_type: CustomProfileFieldTypeId,
135    order: u64,
136    name: String,
137    hint: String,
138    #[serde(skip_serializing_if = "Option::is_none")]
139    field_data: Option<String>,
140    #[serde(skip_serializing_if = "Option::is_none")]
141    display_in_profile_summary: Option<bool>,
142    required: bool,
143    #[serde(rename = "editable_by_user")]
144    editable_by_user: bool,
145}
146
147impl CustomProfileField {
148    /// Dictionary containing the details of a custom profile field configured for this organization.
149    pub const fn new() -> Self {
150        Self {
151            id: 0,
152            field_type: CustomProfileFieldTypeId::new(),
153            order: 0,
154            name: String::new(),
155            hint: String::new(),
156            field_data: None,
157            display_in_profile_summary: None,
158            required: false,
159            editable_by_user: false,
160        }
161    }
162}
163
164field_access! {
165    CustomProfileField {
166        /// The ID of the custom profile field. This will be referenced in the custom
167        /// profile fields section of user objects.
168        id: u64,
169        /// An integer indicating the type of the custom profile field, which determines
170        /// how it is configured and displayed to users.
171        field_type: CustomProfileFieldTypeId,
172        /// Custom profile fields are displayed in both settings UI and UI showing
173        /// users' profiles in increasing `order`.
174        order: u64,
175        /// Whether an organization administrator has configured this profile field
176        /// as required.
177        ///
178        /// Because the required property is mutable, clients cannot assume that a
179        /// required custom profile field has a value. The Zulip web application
180        /// displays a prominent banner to any user who has not set a value for a
181        /// required field.
182        ///
183        /// **Changes**: New in Zulip 9.0 (feature level 244).
184        required: bool,
185        /// Whether regular users can edit this profile field on their own account.
186        ///
187        /// Note that organization administrators can edit custom profile fields
188        /// for any user regardless of this setting.
189        ///
190        /// **Changes**: New in Zulip 10.0 (feature level 296).
191        editable_by_user: bool,
192    }
193}
194
195field_access! {
196    CustomProfileField {
197        /// The name of the custom profile field.
198        name: cow { str },
199        /// The help text to be displayed for the custom profile field in
200        /// user-facing settings UI for configuring custom profile fields.
201        hint: cow { str },
202    }
203}
204
205field_access! {
206    CustomProfileField {
207        /// Whether the custom profile field, display or not on the user card.
208        ///
209        /// Currently it's value not allowed to be `true` of `Long text` and `Person picker` [profile field types](https://zulip.com/help/custom-profile-fields#profile-field-types).
210        ///
211        /// This field is only included when its value is `true`.
212        ///
213        /// **Changes**: New in Zulip 6.0 (feature level 146).
214        display_in_profile_summary: option { bool },
215    }
216}
217
218field_access! {
219    CustomProfileField {
220        /// Field types 3 (List of options) and 7 (External account) support storing
221        /// additional configuration for the field type in the `field_data` attribute.
222        ///
223        /// For field type 3 (List of options), this attribute is a JSON dictionary
224        /// defining the choices and the order they will be displayed in the dropdown
225        /// UI for individual users to select an option.
226        ///
227        /// The interface for field type 7 is not yet stabilized.
228        field_data: option_cow_deref { str, String },
229    }
230}
231
232impl_cow!(CustomProfileField);
233impl_default!(CustomProfileField);
234impl_display!(CustomProfileField);
235
236#[cfg(test)]
237mod tests {
238    use super::*;
239
240    #[test]
241    fn test_custom_profile_field() {
242        let id = 1;
243        let field_type = CustomProfileFieldTypeId::new();
244        let order = 2;
245        let name = "test name";
246        let hint = "test hint";
247        let field_data = "test field data";
248        let display_in_profile_summary = true;
249        let required = false;
250        let editable_by_user = true;
251
252        let field = CustomProfileField::new()
253            .with_id(id)
254            .with_field_type(field_type)
255            .with_order(order)
256            .with_name(name)
257            .with_hint(hint)
258            .with_field_data(field_data)
259            .with_display_in_profile_summary(display_in_profile_summary)
260            .with_required(required)
261            .with_editable_by_user(editable_by_user);
262
263        assert_eq!(field.id(), id);
264        assert_eq!(field.field_type(), field_type);
265        assert_eq!(field.order(), order);
266        assert_eq!(field.name(), name);
267        assert_eq!(field.hint(), hint);
268        assert_eq!(field.field_data(), Some(field_data));
269        assert_eq!(
270            field.display_in_profile_summary(),
271            Some(display_in_profile_summary)
272        );
273        assert_eq!(field.required(), required);
274        assert_eq!(field.editable_by_user(), editable_by_user);
275    }
276}