pub fn patch_task_line(
source: &str,
line_number: u32,
expected: &str,
status: TaskStatus,
now: Option<NaiveDateTime>,
) -> Result<PatchedSource, TaskPatchError>Expand description
Rewrites one task line of a markdown source, checking first that the line is still what the caller last saw.
This is the whole body of POST /.mbr/task, minus the I/O: the caller reads
the file, hands the text here, and writes back PatchedSource::source.
Keeping it pure is what makes the awkward parts — line addressing, the
terminator, the expected comparison — testable without a filesystem.
line_number is 1-based. expected is compared to the line on disk
byte-for-byte modulo the line terminator, so a client that captured the
line with or without its \n (or \r\n) is treated the same.
Only the one line is touched: no other byte of source moves, the patched
line keeps its own terminator, and a file that did not end in a newline still
does not.
§Examples
use chrono::NaiveDate;
use mbr_core::tasks::{TaskStatus, patch_task_line};
let now = NaiveDate::from_ymd_opt(2026, 8, 4)
.and_then(|d| d.and_hms_opt(14, 32, 0))
.unwrap();
let source = "# Notes\r\n- [ ] ship it\r\n- [ ] later\r\n";
let patched =
patch_task_line(source, 2, "- [ ] ship it", TaskStatus::Done, Some(now)).unwrap();
assert_eq!(patched.text, "- [x] ship it @done(2026-08-04 14:32)");
assert_eq!(
patched.source,
"# Notes\r\n- [x] ship it @done(2026-08-04 14:32)\r\n- [ ] later\r\n"
);