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