ralph/queue/operations/edit/
key.rs1use crate::contracts::Task;
16use anyhow::{Result, bail};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum TaskEditKey {
20 Title,
21 Description,
22 Status,
23 Priority,
24 Tags,
25 Scope,
26 Evidence,
27 Plan,
28 Notes,
29 Request,
30 DependsOn,
31 Blocks,
32 RelatesTo,
33 Duplicates,
34 CustomFields,
35 Agent,
36 CreatedAt,
37 UpdatedAt,
38 CompletedAt,
39 StartedAt,
40 ScheduledStart,
41 EstimatedMinutes,
42 ActualMinutes,
43}
44
45impl TaskEditKey {
46 pub fn as_str(self) -> &'static str {
47 match self {
48 TaskEditKey::Title => "title",
49 TaskEditKey::Description => "description",
50 TaskEditKey::Status => "status",
51 TaskEditKey::Priority => "priority",
52 TaskEditKey::Tags => "tags",
53 TaskEditKey::Scope => "scope",
54 TaskEditKey::Evidence => "evidence",
55 TaskEditKey::Plan => "plan",
56 TaskEditKey::Notes => "notes",
57 TaskEditKey::Request => "request",
58 TaskEditKey::DependsOn => "depends_on",
59 TaskEditKey::Blocks => "blocks",
60 TaskEditKey::RelatesTo => "relates_to",
61 TaskEditKey::Duplicates => "duplicates",
62 TaskEditKey::CustomFields => "custom_fields",
63 TaskEditKey::Agent => "agent",
64 TaskEditKey::CreatedAt => "created_at",
65 TaskEditKey::UpdatedAt => "updated_at",
66 TaskEditKey::CompletedAt => "completed_at",
67 TaskEditKey::StartedAt => "started_at",
68 TaskEditKey::ScheduledStart => "scheduled_start",
69 TaskEditKey::EstimatedMinutes => "estimated_minutes",
70 TaskEditKey::ActualMinutes => "actual_minutes",
71 }
72 }
73
74 pub fn is_list_field(self) -> bool {
76 matches!(
77 self,
78 TaskEditKey::Tags
79 | TaskEditKey::Scope
80 | TaskEditKey::Evidence
81 | TaskEditKey::Plan
82 | TaskEditKey::Notes
83 | TaskEditKey::DependsOn
84 | TaskEditKey::Blocks
85 | TaskEditKey::RelatesTo
86 )
87 }
88
89 pub fn format_value(self, task: &Task, list_sep: &str) -> String {
94 match self {
95 TaskEditKey::Title => task.title.clone(),
96 TaskEditKey::Description => task.description.clone().unwrap_or_default(),
97 TaskEditKey::Status => task.status.to_string(),
98 TaskEditKey::Priority => task.priority.to_string(),
99 TaskEditKey::Tags => task.tags.join(list_sep),
100 TaskEditKey::Scope => task.scope.join(list_sep),
101 TaskEditKey::Evidence => task.evidence.join(list_sep),
102 TaskEditKey::Plan => task.plan.join(list_sep),
103 TaskEditKey::Notes => task.notes.join(list_sep),
104 TaskEditKey::Request => task.request.clone().unwrap_or_default(),
105 TaskEditKey::DependsOn => task.depends_on.join(list_sep),
106 TaskEditKey::Blocks => task.blocks.join(list_sep),
107 TaskEditKey::RelatesTo => task.relates_to.join(list_sep),
108 TaskEditKey::Duplicates => task.duplicates.clone().unwrap_or_default(),
109 TaskEditKey::CustomFields => {
110 let pairs: Vec<String> = task
111 .custom_fields
112 .iter()
113 .map(|(k, v)| format!("{}={}", k, v))
114 .collect();
115 pairs.join(list_sep)
116 }
117 TaskEditKey::Agent => task
118 .agent
119 .as_ref()
120 .and_then(|agent| serde_json::to_string(agent).ok())
121 .unwrap_or_default(),
122 TaskEditKey::CreatedAt => task.created_at.clone().unwrap_or_default(),
123 TaskEditKey::UpdatedAt => task.updated_at.clone().unwrap_or_default(),
124 TaskEditKey::CompletedAt => task.completed_at.clone().unwrap_or_default(),
125 TaskEditKey::StartedAt => task.started_at.clone().unwrap_or_default(),
126 TaskEditKey::ScheduledStart => task.scheduled_start.clone().unwrap_or_default(),
127 TaskEditKey::EstimatedMinutes => task
128 .estimated_minutes
129 .map(|m| m.to_string())
130 .unwrap_or_default(),
131 TaskEditKey::ActualMinutes => task
132 .actual_minutes
133 .map(|m| m.to_string())
134 .unwrap_or_default(),
135 }
136 }
137}
138
139impl std::str::FromStr for TaskEditKey {
140 type Err = anyhow::Error;
141
142 fn from_str(value: &str) -> Result<Self> {
143 let normalized = value.trim().to_lowercase();
144 match normalized.as_str() {
145 "title" => Ok(TaskEditKey::Title),
146 "description" => Ok(TaskEditKey::Description),
147 "status" => Ok(TaskEditKey::Status),
148 "priority" => Ok(TaskEditKey::Priority),
149 "tags" => Ok(TaskEditKey::Tags),
150 "scope" => Ok(TaskEditKey::Scope),
151 "evidence" => Ok(TaskEditKey::Evidence),
152 "plan" => Ok(TaskEditKey::Plan),
153 "notes" => Ok(TaskEditKey::Notes),
154 "request" => Ok(TaskEditKey::Request),
155 "depends_on" => Ok(TaskEditKey::DependsOn),
156 "blocks" => Ok(TaskEditKey::Blocks),
157 "relates_to" => Ok(TaskEditKey::RelatesTo),
158 "duplicates" => Ok(TaskEditKey::Duplicates),
159 "custom_fields" => Ok(TaskEditKey::CustomFields),
160 "agent" => Ok(TaskEditKey::Agent),
161 "created_at" => Ok(TaskEditKey::CreatedAt),
162 "updated_at" => Ok(TaskEditKey::UpdatedAt),
163 "completed_at" => Ok(TaskEditKey::CompletedAt),
164 "started_at" => Ok(TaskEditKey::StartedAt),
165 "scheduled_start" => Ok(TaskEditKey::ScheduledStart),
166 "estimated_minutes" => Ok(TaskEditKey::EstimatedMinutes),
167 "actual_minutes" => Ok(TaskEditKey::ActualMinutes),
168 _ => bail!(
169 "Unknown task field: '{}'. Expected one of: title, description, status, priority, tags, scope, evidence, plan, notes, request, depends_on, blocks, relates_to, duplicates, custom_fields, agent, created_at, updated_at, completed_at, started_at, scheduled_start, estimated_minutes, actual_minutes.",
170 value
171 ),
172 }
173 }
174}