use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd)]
#[serde(rename_all = "snake_case")]
pub struct SectionReference {
pub gid: String,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub resource_type: Option<String>,
}
impl SectionReference {
#[must_use]
pub fn label(&self) -> String {
self.name.clone().unwrap_or_else(|| self.gid.clone())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct Section {
pub gid: String,
pub name: String,
#[serde(default)]
pub resource_type: Option<String>,
#[serde(default)]
pub created_at: Option<String>,
#[serde(default)]
pub project: Option<SectionProjectReference>,
#[serde(default)]
pub projects: Vec<SectionProjectReference>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd)]
#[serde(rename_all = "snake_case")]
pub struct SectionProjectReference {
pub gid: String,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub resource_type: Option<String>,
}
impl SectionProjectReference {
#[must_use]
pub fn label(&self) -> String {
self.name.clone().unwrap_or_else(|| self.gid.clone())
}
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct SectionCreateData {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub insert_before: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub insert_after: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct SectionCreateRequest {
pub data: SectionCreateData,
}
#[derive(Debug, Clone, Default, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct SectionUpdateData {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct SectionUpdateRequest {
pub data: SectionUpdateData,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct AddTaskToSectionData {
pub task: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub insert_before: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub insert_after: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct AddTaskToSectionRequest {
pub data: AddTaskToSectionData,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn section_reference_label_uses_name() {
let section = SectionReference {
gid: "123".to_string(),
name: Some("In Progress".to_string()),
resource_type: Some("section".to_string()),
};
assert_eq!(section.label(), "In Progress");
}
#[test]
fn section_reference_label_fallback_to_gid() {
let section = SectionReference {
gid: "456".to_string(),
name: None,
resource_type: Some("section".to_string()),
};
assert_eq!(section.label(), "456");
}
#[test]
fn create_request_serializes_correctly() {
let request = SectionCreateRequest {
data: SectionCreateData {
name: "New Section".to_string(),
insert_before: None,
insert_after: Some("789".to_string()),
},
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"name\":\"New Section\""));
assert!(json.contains("\"insert_after\":\"789\""));
assert!(!json.contains("insert_before"));
}
#[test]
fn add_task_request_serializes_correctly() {
let request = AddTaskToSectionRequest {
data: AddTaskToSectionData {
task: "task123".to_string(),
insert_before: Some("task456".to_string()),
insert_after: None,
},
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"task\":\"task123\""));
assert!(json.contains("\"insert_before\":\"task456\""));
assert!(!json.contains("insert_after"));
}
}