Code forensics  0.1
Generate historical information about code changes
program_state.hpp
Go to the documentation of this file.
1 
4 #ifndef PROGRAM_STATE_HPP
5 #define PROGRAM_STATE_HPP
6 
7 #include <unordered_set>
8 #include <algorithm>
9 #include <chrono>
10 
11 #include <boost/log/trivial.hpp>
12 #include <boost/date_time/gregorian/gregorian.hpp>
13 #include <boost/date_time/posix_time/posix_time.hpp>
14 #include <boost/describe.hpp>
15 #include <boost/mp11.hpp>
16 
17 #include "common.hpp"
18 #include "git_interface.hpp"
19 #include "git_ir.hpp"
20 
21 using Logger = boost::log::sources::severity_logger<
22  boost::log::trivial::severity_level>;
23 
24 using Date = boost::gregorian::date;
25 using PTime = boost::posix_time::ptime;
26 using TimeDuration = boost::posix_time::time_duration;
27 namespace stime = std::chrono;
28 namespace bd = boost::describe;
29 
30 template <class T>
31 struct fmt::formatter<
32  T,
33  char,
34  std::enable_if_t<
35  boost::describe::has_describe_bases<T>::value &&
36  boost::describe::has_describe_members<T>::value &&
37  !std::is_union<T>::value>> {
38  constexpr auto parse(format_parse_context& ctx) {
39  auto it = ctx.begin(), end = ctx.end();
40 
41  if (it != end && *it != '}') {
42  ctx.error_handler().on_error("invalid format");
43  }
44 
45  return it;
46  }
47 
48  auto format(T const& t, format_context& ctx) const {
49  using namespace boost::describe;
50 
51  using Bd = describe_bases<T, mod_any_access>;
52  using Md = describe_members<T, mod_any_access>;
53 
54  auto out = ctx.out();
55 
56  *out++ = '{';
57 
58  bool first = true;
59 
60  boost::mp11::mp_for_each<Bd>([&](auto D) {
61  if (!first) { *out++ = ','; }
62 
63  first = false;
64 
65  out = fmt::format_to(
66  out, " {}", (typename decltype(D)::type const&)t);
67  });
68 
69  boost::mp11::mp_for_each<Md>([&](auto D) {
70  if (!first) { *out++ = ','; }
71 
72  first = false;
73 
74  out = fmt::format_to(out, " .{}={}", D.name, t.*D.pointer);
75  });
76 
77  if (!first) { *out++ = ' '; }
78 
79  *out++ = '}';
80 
81  return out;
82  }
83 };
84 
90 template <class T>
91 struct fmt::formatter<
92  T,
93  char,
94  std::enable_if_t<
95  boost::describe::has_describe_enumerators<T>::value>> {
96  private:
97  using U = std::underlying_type_t<T>;
98 
99  fmt::formatter<fmt::string_view, char> sf_;
100  fmt::formatter<U, char> nf_;
101 
102  public:
103  constexpr auto parse(format_parse_context& ctx) {
104  auto i1 = sf_.parse(ctx);
105  auto i2 = nf_.parse(ctx);
106 
107  if (i1 != i2) { ctx.error_handler().on_error("invalid format"); }
108 
109  return i1;
110  }
111 
112  auto format(T const& t, format_context& ctx) const {
113  char const* s = boost::describe::enum_to_string(t, 0);
114 
115  if (s) {
116  return sf_.format(s, ctx);
117  } else {
118  return nf_.format(static_cast<U>(t), ctx);
119  }
120  }
121 };
122 
125 enum class Analytics {
126  BlameBurndown,
129  Commits
131 };
132 
133 BOOST_DESCRIBE_ENUM(Analytics, BlameBurndown, Commits, CommitDiffInfo);
134 
135 
138 template <typename E>
139 concept IsDescribedEnum = bd::has_describe_enumerators<E>::value;
140 
142 template <>
143 struct fmt::formatter<Date> : fmt::formatter<Str> {
144  auto format(CR<Date> date, fmt::format_context& ctx) const {
145  return fmt::formatter<Str>::format(
146  boost::gregorian::to_iso_extended_string(date), ctx);
147  }
148 };
149 
150 
152 template <>
153 struct fmt::formatter<PTime> : fmt::formatter<Str> {
154  auto format(CR<PTime> time, fmt::format_context& ctx) const {
155  return fmt::formatter<Str>::format(
156  boost::posix_time::to_iso_extended_string(time), ctx);
157  }
158 };
159 
164  bool use_subprocess = true;
167  enum threading_mode { async, defer, sequential };
168  threading_mode use_threading = threading_mode::async;
170  Str repo;
172  Str heads;
173  Vec<Analytics> analytics;
174 
175  Str db_path;
178 
180  Func<bool(CR<Str>)> allow_path;
182  Func<int(CR<PTime>)> get_commit_period;
183  Func<int(CR<PTime>)> get_sampled_period;
185  Func<bool(CR<PTime>, CR<Str>, CR<Str>)> allow_sample;
186 
187  bool use_analytics(Analytics which) const {
188  return analytics.empty() ||
189  std::find(analytics.begin(), analytics.end(), which) !=
190  analytics.end();
191  }
192 };
193 
195 using TimePoint = stime::time_point<stime::system_clock>;
196 
197 
199 struct walker_state {
200  CP<walker_config> config;
201 
202  git_revwalk* walker;
204  git_repository* repo;
205 
207  Vec<git_oid> full_commits;
208  std::unordered_map<git_oid, ir::CommitId> commit_ids;
211  std::unordered_map<git_oid, int> rev_index;
213  std::unordered_map<git_oid, int> rev_periods;
214 
219  CR<git_oid> oid,
220  int period
221  ) {
222  rev_index.insert({oid, full_commits.size()});
223  rev_periods.insert({oid, period});
224  full_commits.push_back(oid);
225  }
226 
227  void add_id_mapping(CR<git_oid> oid, ir::CommitId id) {
228  commit_ids.insert({oid, id});
229  }
230 
231  ir::CommitId get_id(CR<git_oid> oid) { return commit_ids.at(oid); }
232 
233 
237  Opt<int> get_period(CR<git_oid> commit) const noexcept {
238  // NOTE dynamically patching table of missing commits each time an
239  // unknown is encountered is possible, but undesirable.
240  auto found = rev_periods.find(commit);
241  if (found != rev_periods.end()) {
242  return Opt<int>{found->second};
243  } else {
244  return Opt<int>{};
245  }
246  }
247 
250  int get_period(CR<git_oid> commit, CR<git_oid> line) const noexcept {
251  auto lp = get_period(line);
252  auto cp = get_period(commit);
253  return lp.value_or(cp.value());
254  }
255 
262  CR<git_oid> commit_id,
263  CR<git_oid> line_change_id) const -> bool {
264  auto commit = get_period(commit_id);
265  auto line = get_period(line_change_id);
266  if (line) {
267  return line.value() == commit.value();
268  } else {
269  return true;
270  }
271  }
272 
274  std::unordered_map<git_oid, ir::CommitId> sampled_commits;
275 
277  std::mutex m;
280  SPtr<Logger> logger;
281 };
282 
283 #endif // PROGRAM_STATE_HPP
fmt::formatter< T, char, std::enable_if_t< boost::describe::has_describe_enumerators< T >::value > >::parse
constexpr auto parse(format_parse_context &ctx)
Definition: program_state.hpp:103
walker_state::logger
SPtr< Logger > logger
main application logger entry
Definition: program_state.hpp:280
walker_config::heads
Str heads
Which repository branch to use.
Definition: program_state.hpp:172
walker_config::allow_path
Func< bool(CR< Str >)> allow_path
Allow processing of a specific path in the repository.
Definition: program_state.hpp:180
walker_config::get_sampled_period
Func< int(CR< PTime >)> get_sampled_period
Definition: program_state.hpp:183
walker_config::log_progress_bars
bool log_progress_bars
Definition: program_state.hpp:177
walker_state::get_id
ir::CommitId get_id(CR< git_oid > oid)
Definition: program_state.hpp:231
git_interface.hpp
BOOST_DESCRIBE_ENUM
BOOST_DESCRIBE_ENUM(Analytics, BlameBurndown, Commits, CommitDiffInfo)
walker_state::rev_periods
std::unordered_map< git_oid, int > rev_periods
Mapping from the commits to the analysis periods they are in.
Definition: program_state.hpp:213
walker_state::sampled_commits
std::unordered_map< git_oid, ir::CommitId > sampled_commits
List of commits that were selected for the processing run.
Definition: program_state.hpp:274
fmt::formatter< T, char, std::enable_if_t< boost::describe::has_describe_bases< T >::value &&boost::describe::has_describe_members< T >::value &&!std::is_union< T >::value > >::format
auto format(T const &t, format_context &ctx) const
Definition: program_state.hpp:48
walker_config::db_path
Str db_path
Definition: program_state.hpp:175
walker_state::commit_ids
std::unordered_map< git_oid, ir::CommitId > commit_ids
Definition: program_state.hpp:208
walker_config::get_commit_period
Func< int(CR< PTime >)> get_commit_period
Get integer index of the period for Date.
Definition: program_state.hpp:182
fmt::formatter< Date >::format
auto format(CR< Date > date, fmt::format_context &ctx) const
Definition: program_state.hpp:144
fmt::formatter< T, char, std::enable_if_t< boost::describe::has_describe_bases< T >::value &&boost::describe::has_describe_members< T >::value &&!std::is_union< T >::value > >::parse
constexpr auto parse(format_parse_context &ctx)
Definition: program_state.hpp:38
walker_config::repo
Str repo
Current project root path (absolute path)
Definition: program_state.hpp:170
Analytics::Commits
@ Commits
Only information about commits.
walker_config::allow_sample
Func< bool(CR< PTime >, CR< Str >, CR< Str >)> allow_sample
Check whether commits at the specified date should be analysed.
Definition: program_state.hpp:185
TimePoint
stime::time_point< stime::system_clock > TimePoint
stdlib time point alias
Definition: program_state.hpp:195
walker_state::full_commits
Vec< git_oid > full_commits
Ordered list of commits that were considered for the processing run.
Definition: program_state.hpp:207
Analytics::CommitDiffInfo
@ CommitDiffInfo
fmt::formatter< T, char, std::enable_if_t< boost::describe::has_describe_enumerators< T >::value > >::nf_
fmt::formatter< U, char > nf_
Definition: program_state.hpp:100
walker_config::use_analytics
bool use_analytics(Analytics which) const
Definition: program_state.hpp:187
PTime
boost::posix_time::ptime PTime
Definition: program_state.hpp:25
walker_state::content
ir::content_manager * content
Definition: program_state.hpp:278
walker_state::add_id_mapping
void add_id_mapping(CR< git_oid > oid, ir::CommitId id)
Definition: program_state.hpp:227
walker_state::config
CP< walker_config > config
Definition: program_state.hpp:200
walker_state::get_period
Opt< int > get_period(CR< git_oid > commit) const noexcept
Definition: program_state.hpp:237
walker_state::consider_changed
auto consider_changed(CR< git_oid > commit_id, CR< git_oid > line_change_id) const -> bool
Definition: program_state.hpp:261
walker_config
runtime configuration state object
Definition: program_state.hpp:161
Analytics
Analytics
Different modes of repository analytics enabled in the application. Mapped to the --analytics command...
Definition: program_state.hpp:125
walker_state
Mutable state passed around walker configurations.
Definition: program_state.hpp:199
walker_state::get_period
int get_period(CR< git_oid > commit, CR< git_oid > line) const noexcept
get period the line was attributed to, otherwise fall back to the commit period
Definition: program_state.hpp:250
fmt::formatter< T, char, std::enable_if_t< boost::describe::has_describe_enumerators< T >::value > >::format
auto format(T const &t, format_context &ctx) const
Definition: program_state.hpp:112
std
Definition: git_interface.hpp:165
Logger
logging::sources::severity_logger< logging::trivial::severity_level > Logger
Definition: logging.hpp:100
walker_state::rev_index
std::unordered_map< git_oid, int > rev_index
Definition: program_state.hpp:211
walker_state::repo
git_repository * repo
Current git repository.
Definition: program_state.hpp:204
walker_config::threading_mode
threading_mode
Definition: program_state.hpp:167
ir::content_manager
Main store for repository analysis.
Definition: git_ir.hpp:238
fmt::formatter< T, char, std::enable_if_t< boost::describe::has_describe_enumerators< T >::value > >::U
std::underlying_type_t< T > U
Definition: program_state.hpp:97
fmt::formatter< T, char, std::enable_if_t< boost::describe::has_describe_enumerators< T >::value > >::sf_
fmt::formatter< fmt::string_view, char > sf_
Definition: program_state.hpp:99
commit_id
const git_oid * commit_id(const git_commit *commit)
Definition: gitwrap.hpp:3841
IsDescribedEnum
concept IsDescribedEnum
Convenience concept for interfacing with 'described' enumeration types.
Definition: program_state.hpp:139
walker_config::analytics
Vec< Analytics > analytics
Definition: program_state.hpp:173
walker_state::m
std::mutex m
common mutex for synchronizing content manager mutations
Definition: program_state.hpp:277
Analytics::BlameBurndown
@ BlameBurndown
Date
boost::gregorian::date Date
Definition: program_state.hpp:24
walker_state::add_full_commit
void add_full_commit(CR< git_oid > oid, int period)
Definition: program_state.hpp:218
walker_state::walker
git_revwalk * walker
Definition: program_state.hpp:202
walker_config::try_incremental
bool try_incremental
Definition: program_state.hpp:176
fmt::formatter< PTime >::format
auto format(CR< PTime > time, fmt::format_context &ctx) const
Definition: program_state.hpp:154
git_ir.hpp
TimeDuration
boost::posix_time::time_duration TimeDuration
Definition: program_state.hpp:26