asana_cli/models/
custom_field.rs1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::BTreeMap;
6
7#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
9#[serde(rename_all = "snake_case")]
10pub enum CustomFieldType {
11 #[default]
13 Text,
14 Number,
16 Enum,
18 MultiEnum,
20 Date,
22 People,
24 Percent,
26 Currency,
28 #[serde(other)]
30 Unknown,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
35#[serde(rename_all = "snake_case")]
36pub struct CustomFieldEnumOption {
37 pub gid: String,
39 pub name: String,
41 #[serde(default)]
43 pub color: Option<String>,
44 #[serde(default)]
46 pub enabled: Option<bool>,
47 #[serde(default)]
49 pub resource_type: Option<String>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
54#[serde(rename_all = "snake_case")]
55pub struct CustomFieldDateValue {
56 #[serde(default)]
58 pub date: Option<String>,
59 #[serde(default)]
61 pub start_on: Option<String>,
62 #[serde(default)]
64 pub due_on: Option<String>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
69#[serde(rename_all = "snake_case")]
70pub struct CustomField {
71 pub gid: String,
73 pub name: String,
75 #[serde(default)]
77 pub resource_type: Option<String>,
78 #[serde(rename = "type")]
80 pub field_type: CustomFieldType,
81 #[serde(default)]
83 pub description: Option<String>,
84 #[serde(default)]
86 pub enabled: Option<bool>,
87 #[serde(default)]
89 pub display_value: Option<String>,
90 #[serde(default)]
92 pub text_value: Option<String>,
93 #[serde(default)]
95 pub number_value: Option<f64>,
96 #[serde(default)]
98 pub percent_value: Option<f64>,
99 #[serde(default)]
101 pub currency_code: Option<String>,
102 #[serde(default)]
104 pub enum_value: Option<CustomFieldEnumOption>,
105 #[serde(default)]
107 pub multi_enum_values: Vec<CustomFieldEnumOption>,
108 #[serde(default)]
110 pub date_value: Option<CustomFieldDateValue>,
111 #[serde(default)]
113 pub people_value: Vec<String>,
114 #[serde(flatten)]
116 pub extra: BTreeMap<String, Value>,
117}
118
119#[derive(Debug, Clone)]
121pub enum CustomFieldValue {
122 Text(String),
124 Number(f64),
126 Bool(bool),
128 EnumOption(String),
130 MultiEnum(Vec<String>),
132 Date(String),
134 DateRange {
136 start_on: Option<String>,
138 due_on: Option<String>,
140 },
141 Json(Value),
143}
144
145impl CustomFieldValue {
146 #[must_use]
148 pub fn into_value(self) -> Value {
149 match self {
150 Self::Text(value) => Value::String(value),
151 Self::Number(value) => {
152 serde_json::Number::from_f64(value).map_or(Value::Null, Value::Number)
153 }
154 Self::Bool(value) => Value::Bool(value),
155 Self::EnumOption(gid) => Value::String(gid),
156 Self::MultiEnum(gids) => Value::Array(gids.into_iter().map(Value::String).collect()),
157 Self::Date(date) => Value::String(date),
158 Self::DateRange { start_on, due_on } => {
159 let mut map = serde_json::Map::new();
160 if let Some(start_on) = start_on {
161 map.insert("start_on".into(), Value::String(start_on));
162 }
163 if let Some(due_on) = due_on {
164 map.insert("due_on".into(), Value::String(due_on));
165 }
166 Value::Object(map)
167 }
168 Self::Json(value) => value,
169 }
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn converts_text_value() {
179 let value = CustomFieldValue::Text("hello".into()).into_value();
180 assert_eq!(value, Value::String("hello".into()));
181 }
182
183 #[test]
184 fn converts_multi_enum_value() {
185 let value = CustomFieldValue::MultiEnum(vec!["111".into(), "222".into()]).into_value();
186 assert_eq!(
187 value,
188 Value::Array(vec![
189 Value::String("111".into()),
190 Value::String("222".into())
191 ])
192 );
193 }
194
195 #[test]
196 fn converts_date_range() {
197 let value = CustomFieldValue::DateRange {
198 start_on: Some("2024-01-01".into()),
199 due_on: None,
200 }
201 .into_value();
202 assert!(
203 value
204 .as_object()
205 .and_then(|map| map.get("start_on"))
206 .is_some()
207 );
208 }
209}