Skip to main content

singularity_cli/models/
task_group.rs

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