• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

antonvw / wex / 20411251037

21 Dec 2025 02:28PM UTC coverage: 64.372% (+0.03%) from 64.344%
20411251037

push

github

antonvw
parsing seems OK, and action is set by semantic action

18618 of 31688 branches covered (58.75%)

Branch coverage included in aggregate %.

14786 of 20204 relevant lines covered (73.18%)

1511.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.48
/src/factory/unified-diff-parser.cpp
1
////////////////////////////////////////////////////////////////////////////////
2
// Name:      unified-diff-parser.cpp
3
// Purpose:   Implementation of unified_diff_parser
4
//            https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html
5
// Author:    Anton van Wezenbeek
6
// Copyright: (c) 2025 Anton van Wezenbeek
7
////////////////////////////////////////////////////////////////////////////////
8

9
#include <boost/parser/parser.hpp>
10
#include <wex/core/log.h>
11
#include <wex/factory/unified-diff.h>
12

13
#include "unified-diff-parser.h"
14

15
namespace bp = boost::parser;
16

17
enum line_action_t
18
{
19
  ACTION_DEL,
20
  ACTION_ADD,
21
  ACTION_BOTH,
22
  ACTION_UNKNOWN,
23
};
24

25
struct diff
26
{
27
  std::string path_a, path_b;
28

29
  std::vector<std::vector<std::variant<std::pair<int, int>, int>>> hunks;
30

31
  std::vector<std::string> files;
32
};
33

34
wex::factory::unified_diff_parser::unified_diff_parser(unified_diff* diff)
3✔
35
  : m_diff(diff)
3✔
36
{
37
  m_diff->m_diffs = 0;
3✔
38
  m_diff->m_type  = unified_diff::diff_t::UNKNOWN;
3✔
39
}
3✔
40

41
bool wex::factory::unified_diff_parser::parse()
3✔
42
{
43
  bp::symbols<int> const line_actions = {
44
    {"-", ACTION_DEL},
×
45
    {"+", ACTION_ADD},
×
46
    {" ", ACTION_BOTH}};
3✔
47

48
  int action = ACTION_UNKNOWN;
3✔
49

50
  auto const action_diff = [this](auto& ctx)
51
  {
52
    log::debug("unified_diff_parser") << "action_diff";
53
    diff x;
54

55
    m_diff->m_path[0] = wex::path(x.path_a);
56
    m_diff->m_path[1] = wex::path(x.path_b);
57

58
    for (const auto& hunk : x.hunks)
59
    {
60
      int index = 0;
61

62
      for (const auto& number : hunk)
63
      {
64
        if (const auto* val = std::get_if<std::pair<int, int>>(&number); val)
65
        {
66
          m_diff->m_range[index]     = val->first;
67
          m_diff->m_range[index + 1] = val->second;
68
        }
69
        else
70
        {
71
          m_diff->m_range[index]     = std::get<int>(number);
72
          m_diff->m_range[index + 1] = 1;
73
        }
74

75
        index += 2;
76
      }
77

78
      m_diff->m_type =
79
        (m_diff->m_type == unified_diff::diff_t::UNKNOWN ?
80
           unified_diff::diff_t::FIRST :
81
           unified_diff::diff_t::OTHER);
82

83
      m_diff->report_diff();
84
    }
85
  };
3✔
86

87
  auto action_set = [&action](auto& ctx)
2✔
88
  {
89
    action = _attr(ctx);
2✔
90
    log::debug("unified_diff_parser") << "action_set" << action;
2✔
91
  };
5✔
92

93
  // (Skip the first lines)
94
  // The unified output format starts with a two-line header:
95
  // --- from-file from-file-modification-time
96
  // +++ to-file to-file-modification-time
97
  // Next come one or more hunks of differences:
98
  // @@ from-file-line-numbers to-file-line-numbers @@
99
  // line-from-either-file
100
  // line-from-either-file...
101

102
  auto const parser_diff_lines =
103
    bp::lexeme[+(line_actions[action_set] >> +(bp::char_ - bp::eol))];
3✔
104

105
  auto const parser_hunk =
106
    bp::lit("@@") >> bp::repeat(2)[bp::int_ >> ',' >> bp::int_ | bp::int_] >>
3✔
107
      bp::lit("@@") >> bp::lexeme[+(bp::char_ - bp::eol)] >> parser_diff_lines;
3✔
108

109
  auto const parser_diff =
110
    bp::lit("--- a/") >> +(bp::char_ - "+++ b/") >> bp::lit("+++ b/") >>
3✔
111
    +(bp::char_ - "@@") >>
3✔
112
    +parser_hunk;
3✔
113

114
  auto const parser_skip = bp::omit[*(bp::char_ - "--- a/")];
3✔
115

116
  auto const parser_all = parser_skip >> +parser_diff;
3✔
117

118
  if (const auto result = bp::parse(
3✔
119
        m_diff->input(),
3✔
120
        parser_all,
121
        bp::ws,
122
        log::get_level() == log::level_t::TRACE ? bp::trace::on :
3✔
123
                                                  bp::trace::off);
6!
124
      result)
3✔
125
  {
126
    return true;
2✔
127
  }
3✔
128

129
  return false;
1✔
130
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc