@@ -119,6 +119,20 @@ class datarecorder
119119 m_recording_filename = filename;
120120 }
121121
122+ // / Set the mismatch template used for mismatch HTML output.
123+ // /
124+ // / If this path is relative, it will be resolved by searching backwards
125+ // / from the current working directory.
126+ // /
127+ // / If this path is provided and cannot be found, recording will fail.
128+ void set_mismatch_template (std::filesystem::path mismatch_template)
129+ {
130+ VERIFY (!mismatch_template.empty (),
131+ " Mismatch template path must not be empty" , mismatch_template);
132+
133+ m_mismatch_template = mismatch_template;
134+ }
135+
122136 // / Set the callback that will be called when a mismatch is found.
123137 // /
124138 // / If no mismatch handler is set, a default mismatch handler will be used.
@@ -237,33 +251,43 @@ class datarecorder
237251
238252 void determine_mismatch_handler ()
239253 {
240- auto visualizer = find_relative_path (" visualizer/recording_diff.html" );
254+ tl::expected<std::filesystem::path, poke::error> mismatch_template =
255+ find_relative_path (
256+ m_mismatch_template.value_or (" visualizer/recording_diff.html" ));
241257
242- if (visualizer )
258+ if (mismatch_template )
243259 {
244260 m_monitor.log (poke::log_level::debug,
245- poke::log::str{" message" , " Using diff visualizer " },
246- poke::log::str{" path" , visualizer ->string ()});
261+ poke::log::str{" message" , " Using mismatch template " },
262+ poke::log::str{" path" , mismatch_template ->string ()});
247263
248- m_on_mismatch = [this , visualizer](mismatch_info mismatch)
264+ m_on_mismatch = [this , mismatch_template = *mismatch_template](
265+ mismatch_info mismatch)
249266 {
250267 // Call the diff handler
251- return diff_mismatch_handler (*visualizer , mismatch);
268+ return diff_mismatch_handler (mismatch_template , mismatch);
252269 };
270+ return ;
253271 }
254- else
255- {
256- m_monitor.log (
257- poke::log_level::debug,
258- poke::log::str{" message" , " Using default mismatch handler" },
259- poke::log::str{" path" , visualizer.error ().message ()});
260272
261- m_on_mismatch = [this ](mismatch_info mismatch)
262- {
263- // Call the default handler
264- return default_mismatch_handler (mismatch);
265- };
273+ if (m_mismatch_template)
274+ {
275+ VERIFY (mismatch_template,
276+ " Could not find configured mismatch template" ,
277+ m_mismatch_template.value (),
278+ mismatch_template.error ().message ());
266279 }
280+
281+ m_monitor.log (
282+ poke::log_level::debug,
283+ poke::log::str{" message" , " Using default mismatch handler" },
284+ poke::log::str{" path" , mismatch_template.error ().message ()});
285+
286+ m_on_mismatch = [this ](mismatch_info mismatch)
287+ {
288+ // Call the default handler
289+ return default_mismatch_handler (mismatch);
290+ };
267291 }
268292
269293 auto determine_mismatch_path () -> std::filesystem::path
@@ -435,6 +459,18 @@ class datarecorder
435459 std::regex oldTextPattern (R"( (const\s+oldText\s*=\s*`)([^`]*)(`;))" );
436460 std::regex newTextPattern (R"( (const\s+newText\s*=\s*`)([^`]*)(`;))" );
437461
462+ if (!std::regex_search (file_content, oldTextPattern) ||
463+ !std::regex_search (file_content, newTextPattern))
464+ {
465+ return poke::make_error (
466+ std::make_error_code (std::errc::invalid_argument),
467+ poke::log::str{" message" ,
468+ " Mismatch template must contain both `const "
469+ " oldText = `...`;` "
470+ " and `const newText = `...`;` markers" },
471+ poke::log::str{" template_path" , recording_diff_html.string ()});
472+ }
473+
438474 file_content = std::regex_replace (file_content, oldTextPattern,
439475 " $1" + escaped_recording_data + " $3" );
440476 file_content = std::regex_replace (file_content, newTextPattern,
@@ -479,6 +515,7 @@ class datarecorder
479515
480516 std::optional<std::string> m_recording_filename;
481517 std::optional<std::filesystem::path> m_recording_dir;
518+ std::optional<std::filesystem::path> m_mismatch_template;
482519 std::optional<std::function<poke::error(mismatch_info)>> m_on_mismatch;
483520};
484521
0 commit comments