///////////////////////////////////////////////////////////////////////////
| 580 | |
| 581 | //////////////////////////////////////////////////////////////////////////////// |
| 582 | std::optional<std::string> CmdInfo::formatForInfo(const std::vector<Operation>& operations, |
| 583 | size_t group_start, size_t group_end, |
| 584 | const std::string& dateformat, long& last_start) { |
| 585 | std::stringstream out; |
| 586 | for (auto i = group_start; i < group_end; i++) { |
| 587 | auto& operation = operations[i]; |
| 588 | assert(operation.is_update()); |
| 589 | |
| 590 | // Extract the parts of the Update operation. |
| 591 | std::string prop = operation.get_property(); |
| 592 | std::optional<std::string> value = operation.get_value(); |
| 593 | std::optional<std::string> old_value = operation.get_old_value(); |
| 594 | Datetime timestamp(operation.get_timestamp()); |
| 595 | |
| 596 | // Never care about modifying the modification time, or the legacy properties `depends` and |
| 597 | // `tags`. |
| 598 | if (prop == "modified" || prop == "depends" || prop == "tags") { |
| 599 | continue; |
| 600 | } |
| 601 | |
| 602 | // Handle property deletions |
| 603 | if (!value && old_value) { |
| 604 | if (Task::isAnnotationAttr(prop)) { |
| 605 | out << format("Annotation '{1}' deleted.\n", *old_value); |
| 606 | } else if (Task::isTagAttr(prop)) { |
| 607 | out << format("Tag '{1}' deleted.\n", Task::attr2Tag(prop)); |
| 608 | } else if (Task::isDepAttr(prop)) { |
| 609 | out << format("Dependency on '{1}' deleted.\n", Task::attr2Dep(prop)); |
| 610 | } else if (prop == "start") { |
| 611 | Datetime started(last_start); |
| 612 | Datetime stopped = timestamp; |
| 613 | |
| 614 | // If any update in this group sets the `end` property, use that instead of the |
| 615 | // timestamp deleting the `start` property as the stop time. |
| 616 | // See https://github.com/GothenburgBitFactory/taskwarrior/issues/2514 |
| 617 | for (auto i = group_start; i < group_end; i++) { |
| 618 | auto& op = operations[i]; |
| 619 | assert(op.is_update()); |
| 620 | if (op.get_property() == "end") { |
| 621 | try { |
| 622 | stopped = op.get_value().value(); |
| 623 | } catch (std::string) { |
| 624 | // Fall back to the 'start' timestamp if its value is un-parseable. |
| 625 | stopped = op.get_timestamp(); |
| 626 | } |
| 627 | } |
| 628 | } |
| 629 | |
| 630 | out << format("{1} deleted (duration: {2}).", Lexer::ucFirst(prop), |
| 631 | Duration(stopped - started).format()) |
| 632 | << "\n"; |
| 633 | } else { |
| 634 | out << format("{1} deleted.\n", Lexer::ucFirst(prop)); |
| 635 | } |
| 636 | } |
| 637 | |
| 638 | // Handle property additions. |
| 639 | if (value && !old_value) { |
nothing calls this directly
no test coverage detected