opendev_runtime/todo/
parsing.rs1use super::TodoStatus;
2
3pub fn parse_status(s: &str) -> Option<TodoStatus> {
8 match s.to_lowercase().trim() {
9 "pending" | "todo" => Some(TodoStatus::Pending),
10 "in_progress" | "doing" | "in-progress" | "in progress" => Some(TodoStatus::InProgress),
11 "completed" | "done" | "complete" => Some(TodoStatus::Completed),
12 _ => None,
13 }
14}
15
16pub fn strip_markdown(text: &str) -> String {
18 text.replace("**", "")
19 .replace("__", "")
20 .replace('*', "")
21 .replace('_', " ")
22 .replace('`', "")
23 .replace("~~", "")
24}
25
26pub fn parse_plan_steps(plan_content: &str) -> Vec<String> {
32 let mut steps = Vec::new();
34 let mut in_steps_section = false;
35
36 for line in plan_content.lines() {
37 let trimmed = line.trim();
38
39 if trimmed.starts_with("## Implementation Steps")
41 || trimmed.starts_with("## Steps")
42 || trimmed.starts_with("## implementation steps")
43 {
44 in_steps_section = true;
45 continue;
46 }
47
48 if in_steps_section && trimmed.starts_with("## ") {
50 break;
51 }
52
53 if in_steps_section
55 && let Some(text) = extract_numbered_step(trimmed)
56 && !text.is_empty()
57 {
58 steps.push(text);
59 }
60 }
61
62 if steps.is_empty() {
64 for line in plan_content.lines() {
65 let trimmed = line.trim();
66 if trimmed.starts_with('#') {
68 continue;
69 }
70 if let Some(text) = extract_numbered_step(trimmed)
71 && !text.is_empty()
72 {
73 steps.push(text);
74 }
75 }
76 }
77
78 steps
79}
80
81fn extract_numbered_step(line: &str) -> Option<String> {
88 let line = line.trim();
89 if line.is_empty() {
90 return None;
91 }
92
93 let mut chars = line.chars();
95 let first = chars.next()?;
96 if !first.is_ascii_digit() {
97 return None;
98 }
99
100 let rest: String = chars.collect();
102 let rest = rest.trim_start_matches(|c: char| c.is_ascii_digit());
103
104 let rest = if let Some(s) = rest.strip_prefix(". ") {
106 s
107 } else if let Some(s) = rest.strip_prefix(") ") {
108 s
109 } else if let Some(s) = rest.strip_prefix(" - ") {
110 s
111 } else {
112 return None;
113 };
114
115 let text = rest.trim();
116 if text.is_empty() {
117 None
118 } else {
119 let text = text.replace("**", "").replace("__", "");
121 Some(text)
122 }
123}
124
125#[cfg(test)]
126#[path = "parsing_tests.rs"]
127mod tests;