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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::{field_access, impl_cow, impl_default, impl_display};
/// Represents the details of the field type with the field type as the name of the
/// property itself. The current supported field types are as follows:
///
/// - `SHORT_TEXT`
/// - `LONG_TEXT`
/// - `DATE` for date-based fields.
/// - `SELECT` for a list of options.
/// - `URL` for links.
/// - `EXTERNAL_ACCOUNT` for external accounts.
/// - `USER` for selecting a user for the field.
/// - `PRONOUNS` for a short text field with convenient typeahead for one's preferred pronouns.
///
/// **Changes**: `PRONOUNS` type added in Zulip 6.0 (feature level 151).
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CustomProfileFieldType {
id: CustomProfileFieldTypeId,
name: String,
}
impl CustomProfileFieldType {
/// Creates a new [CustomProfileFieldType].
pub const fn new() -> Self {
Self {
id: CustomProfileFieldTypeId::new(),
name: String::new(),
}
}
}
field_access! {
CustomProfileFieldType {
/// The ID of the custom profile field type.
id: CustomProfileFieldTypeId,
}
}
field_access! {
CustomProfileFieldType {
name: cow { str },
}
}
impl_cow!(CustomProfileFieldType);
impl_default!(CustomProfileFieldType);
impl_display!(CustomProfileFieldType);
impl From<CustomProfileFieldTypeId> for CustomProfileFieldType {
fn from(val: CustomProfileFieldTypeId) -> Self {
Self {
id: val,
name: val.as_str().into(),
}
}
}
/// Represents the details of the field type with the field type as the name of the property itself.
///
/// The current supported field types are as follows:
///
/// - `SHORT_TEXT`
/// - `LONG_TEXT`
/// - `DATE` for date-based fields.
/// - `SELECT` for a list of options.
/// - `URL` for links.
/// - `EXTERNAL_ACCOUNT` for external accounts.
/// - `USER` for selecting a user for the field.
/// - `PRONOUNS` for a short text field with convenient typeahead for one's preferred pronouns.
///
/// **Changes**: `PRONOUNS` type added in Zulip 6.0 (feature level 151).
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize_repr, Serialize_repr)]
pub enum CustomProfileFieldTypeId {
ShortText = 1,
LongText = 2,
Date = 3,
Select = 4,
Url = 5,
ExternalAccount = 6,
User = 7,
Pronouns = 8,
}
impl CustomProfileFieldTypeId {
/// Represents the short text field type.
pub const SHORT_TEXT: &str = "SHORT_TEXT";
/// Represents the long text field type.
pub const LONG_TEXT: &str = "LONG_TEXT";
/// Represents the date field type.
pub const DATE: &str = "DATE";
/// Represents the select field type.
pub const SELECT: &str = "SELECT";
/// Represents the url field type.
pub const URL: &str = "URL";
/// Represents the external account field type.
pub const EXTERNAL_ACCOUNT: &str = "EXTERNAL_ACCOUNT";
/// Represents the user field type.
pub const USER: &str = "USER";
/// Represents the pronouns field type.
pub const PRONOUNS: &str = "PRONOUNS";
/// Creates a new [CustomProfileFieldTypeId].
pub const fn new() -> Self {
Self::ShortText
}
/// Gets the [CustomProfileFieldTypeId] string representation.
pub const fn as_str(&self) -> &'static str {
match self {
Self::ShortText => Self::SHORT_TEXT,
Self::LongText => Self::LONG_TEXT,
Self::Date => Self::DATE,
Self::Select => Self::SELECT,
Self::Url => Self::URL,
Self::ExternalAccount => Self::EXTERNAL_ACCOUNT,
Self::User => Self::USER,
Self::Pronouns => Self::PRONOUNS,
}
}
}
impl_default!(CustomProfileFieldTypeId);
impl_display!(CustomProfileFieldTypeId);
/// Represents the details of a custom profile field configured for this organization.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CustomProfileField {
id: u64,
#[serde(rename = "type")]
field_type: CustomProfileFieldTypeId,
order: u64,
name: String,
hint: String,
#[serde(skip_serializing_if = "Option::is_none")]
field_data: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
display_in_profile_summary: Option<bool>,
required: bool,
#[serde(rename = "editable_by_user")]
editable_by_user: bool,
}
impl CustomProfileField {
/// Dictionary containing the details of a custom profile field configured for this organization.
pub const fn new() -> Self {
Self {
id: 0,
field_type: CustomProfileFieldTypeId::new(),
order: 0,
name: String::new(),
hint: String::new(),
field_data: None,
display_in_profile_summary: None,
required: false,
editable_by_user: false,
}
}
}
field_access! {
CustomProfileField {
/// The ID of the custom profile field. This will be referenced in the custom
/// profile fields section of user objects.
id: u64,
/// An integer indicating the type of the custom profile field, which determines
/// how it is configured and displayed to users.
field_type: CustomProfileFieldTypeId,
/// Custom profile fields are displayed in both settings UI and UI showing
/// users' profiles in increasing `order`.
order: u64,
/// Whether an organization administrator has configured this profile field
/// as required.
///
/// Because the required property is mutable, clients cannot assume that a
/// required custom profile field has a value. The Zulip web application
/// displays a prominent banner to any user who has not set a value for a
/// required field.
///
/// **Changes**: New in Zulip 9.0 (feature level 244).
required: bool,
/// Whether regular users can edit this profile field on their own account.
///
/// Note that organization administrators can edit custom profile fields
/// for any user regardless of this setting.
///
/// **Changes**: New in Zulip 10.0 (feature level 296).
editable_by_user: bool,
}
}
field_access! {
CustomProfileField {
/// The name of the custom profile field.
name: cow { str },
/// The help text to be displayed for the custom profile field in
/// user-facing settings UI for configuring custom profile fields.
hint: cow { str },
}
}
field_access! {
CustomProfileField {
/// Whether the custom profile field, display or not on the user card.
///
/// 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).
///
/// This field is only included when its value is `true`.
///
/// **Changes**: New in Zulip 6.0 (feature level 146).
display_in_profile_summary: option { bool },
}
}
field_access! {
CustomProfileField {
/// Field types 3 (List of options) and 7 (External account) support storing
/// additional configuration for the field type in the `field_data` attribute.
///
/// For field type 3 (List of options), this attribute is a JSON dictionary
/// defining the choices and the order they will be displayed in the dropdown
/// UI for individual users to select an option.
///
/// The interface for field type 7 is not yet stabilized.
field_data: option_cow_deref { str, String },
}
}
impl_cow!(CustomProfileField);
impl_default!(CustomProfileField);
impl_display!(CustomProfileField);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_custom_profile_field() {
let id = 1;
let field_type = CustomProfileFieldTypeId::new();
let order = 2;
let name = "test name";
let hint = "test hint";
let field_data = "test field data";
let display_in_profile_summary = true;
let required = false;
let editable_by_user = true;
let field = CustomProfileField::new()
.with_id(id)
.with_field_type(field_type)
.with_order(order)
.with_name(name)
.with_hint(hint)
.with_field_data(field_data)
.with_display_in_profile_summary(display_in_profile_summary)
.with_required(required)
.with_editable_by_user(editable_by_user);
assert_eq!(field.id(), id);
assert_eq!(field.field_type(), field_type);
assert_eq!(field.order(), order);
assert_eq!(field.name(), name);
assert_eq!(field.hint(), hint);
assert_eq!(field.field_data(), Some(field_data));
assert_eq!(
field.display_in_profile_summary(),
Some(display_in_profile_summary)
);
assert_eq!(field.required(), required);
assert_eq!(field.editable_by_user(), editable_by_user);
}
}