Parse the existing learnings section into a map of heading → list items. Returns a map like `{"### Repo" => ["- item1", "- item2"], "### Workflow" => ["- tip"]}`. Returns an empty map if no section exists.
(content: &str)
| 227 | /// Returns a map like `{"### Repo" => ["- item1", "- item2"], "### Workflow" => ["- tip"]}`. |
| 228 | /// Returns an empty map if no section exists. |
| 229 | fn parse_existing_section(content: &str) -> HashMap<String, Vec<String>> { |
| 230 | let mut sections: HashMap<String, Vec<String>> = HashMap::new(); |
| 231 | |
| 232 | let start_idx = match content.find(SECTION_START) { |
| 233 | Some(idx) => idx + SECTION_START.len(), |
| 234 | None => return sections, |
| 235 | }; |
| 236 | |
| 237 | let end_idx = match content[start_idx..].find(SECTION_END) { |
| 238 | Some(idx) => start_idx + idx, |
| 239 | None => return sections, |
| 240 | }; |
| 241 | |
| 242 | let section_text = &content[start_idx..end_idx]; |
| 243 | let mut current_heading: Option<String> = None; |
| 244 | |
| 245 | for line in section_text.lines() { |
| 246 | let trimmed = line.trim(); |
| 247 | if trimmed.starts_with("### ") { |
| 248 | current_heading = Some(trimmed.to_string()); |
| 249 | } else if trimmed.starts_with("- ") { |
| 250 | if let Some(ref heading) = current_heading { |
| 251 | sections |
| 252 | .entry(heading.clone()) |
| 253 | .or_default() |
| 254 | .push(trimmed.to_string()); |
| 255 | } |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | sections |
| 260 | } |
| 261 | |
| 262 | /// Collect all existing learning lines (flat list) for dedup checking. |
| 263 | fn read_existing_learnings(content: &str) -> Vec<String> { |
no test coverage detected