Skip to main content

singularity_cli/models/
task_group.rs

1use serde::{Deserialize, Serialize};
2use tabled::Tabled;
3
4fn display_opt(o: &Option<String>) -> String {
5    o.as_deref().unwrap_or("-").to_string()
6}
7
8#[derive(Debug, Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub struct TaskGroupListResponse {
11    pub task_groups: Vec<TaskGroup>,
12}
13
14#[derive(Debug, Deserialize, Tabled)]
15#[tabled(rename_all = "UPPERCASE")]
16pub struct TaskGroup {
17    pub id: String,
18    pub title: String,
19    #[tabled(display_with = "display_opt")]
20    pub parent: Option<String>,
21    #[tabled(skip)]
22    #[serde(rename = "parentOrder")]
23    pub parent_order: Option<f64>,
24    #[tabled(skip)]
25    pub fake: Option<bool>,
26    #[tabled(skip)]
27    #[serde(rename = "modificatedDate")]
28    #[allow(dead_code)]
29    pub modificated_date: Option<String>,
30}
31
32impl TaskGroup {
33    pub fn display_detail(&self) {
34        println!("ID:     {}", self.id);
35        println!("Title:  {}", self.title);
36        if let Some(ref v) = self.parent {
37            println!("Parent: {}", v);
38        }
39        if let Some(v) = self.parent_order {
40            println!("Order:  {}", v);
41        }
42        if let Some(v) = self.fake {
43            println!("Fake:   {}", v);
44        }
45    }
46}
47
48#[derive(Debug, Serialize)]
49pub struct TaskGroupCreate {
50    pub title: String,
51    pub parent: String,
52    #[serde(skip_serializing_if = "Option::is_none", rename = "parentOrder")]
53    pub parent_order: Option<f64>,
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub fake: Option<bool>,
56}
57
58#[derive(Debug, Serialize, Default)]
59pub struct TaskGroupUpdate {
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub title: Option<String>,
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub parent: Option<String>,
64    #[serde(skip_serializing_if = "Option::is_none", rename = "parentOrder")]
65    pub parent_order: Option<f64>,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub fake: Option<bool>,
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn deserialize_task_group_list() {
76        let json = r#"{"taskGroups": [{"id": "Q-1", "title": "Group 1", "parent": "P-1", "parentOrder": 2.0}]}"#;
77        let resp: TaskGroupListResponse = serde_json::from_str(json).unwrap();
78        assert_eq!(resp.task_groups.len(), 1);
79        assert_eq!(resp.task_groups[0].id, "Q-1");
80        assert_eq!(resp.task_groups[0].parent.as_deref(), Some("P-1"));
81        assert_eq!(resp.task_groups[0].parent_order, Some(2.0));
82    }
83
84    #[test]
85    fn deserialize_task_group_minimal() {
86        let json = r#"{"id": "Q-2", "title": "Bare"}"#;
87        let g: TaskGroup = serde_json::from_str(json).unwrap();
88        assert_eq!(g.id, "Q-2");
89        assert!(g.parent.is_none());
90        assert!(g.fake.is_none());
91    }
92
93    #[test]
94    fn serialize_create_camel_case() {
95        let data = TaskGroupCreate {
96            title: "G".to_string(),
97            parent: "P-1".to_string(),
98            parent_order: Some(3.0),
99            fake: None,
100        };
101        let json = serde_json::to_value(&data).unwrap();
102        assert_eq!(json["parentOrder"], 3.0);
103        assert!(json.get("parent_order").is_none());
104        assert!(json.get("fake").is_none());
105    }
106
107    #[test]
108    fn serialize_update_empty() {
109        let data = TaskGroupUpdate::default();
110        let json = serde_json::to_value(&data).unwrap();
111        assert_eq!(json, serde_json::json!({}));
112    }
113}