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