1#![forbid(unsafe_code)]
7#![warn(missing_docs)]
8
9pub mod compare;
10pub mod diff;
11pub mod inspect;
12
13const USAGE: &str = "\
14Tickwise: record, replay, and diff deterministic simulations.
15
16Usage:
17 tickwise inspect <session.rec> show metadata and statistics for a recording
18 tickwise compare <a.rec> <b.rec> find the first divergent tick
19 tickwise diff <a.dump> <b.dump> field-level structural diff of two state dumps
20
21Diff flags:
22 --strict every bit-level float difference counts as exact
23 --epsilon-f32 <x> sub-epsilon threshold for f32, default 1e-5
24 --epsilon-f64 <x> sub-epsilon threshold for f64, default 1e-12
25 --all show every difference instead of the first 100 per tick
26 --no-color plain output, also honored via the NO_COLOR variable
27
28Exit codes for compare and diff: 0 identical, 1 differences found, 2 trouble.
29
30Options:
31 -h, --help show this help
32 -V, --version show the version
33";
34
35fn color_allowed() -> bool {
36 use std::io::IsTerminal;
37 std::env::var_os("NO_COLOR").is_none() && std::io::stdout().is_terminal()
38}
39
40pub fn run(args: &[String]) -> u8 {
43 let Some((command, rest)) = args.split_first() else {
44 eprint!("{USAGE}");
45 return 2;
46 };
47
48 match command.as_str() {
49 "inspect" => match rest {
50 [path] => match inspect::render(path) {
51 Ok(report) => {
52 print!("{}", report.text);
53 u8::from(report.corrupt)
54 }
55 Err(err) => {
56 eprintln!("tickwise inspect: {err}");
57 1
58 }
59 },
60 _ => {
61 eprintln!("usage: tickwise inspect <session.rec>");
62 2
63 }
64 },
65 "compare" => match rest {
66 [a, b] => match compare::render(a, b) {
67 Ok(output) => {
68 print!("{}", output.text);
69 u8::from(output.diverged)
70 }
71 Err(err) => {
72 eprintln!("tickwise compare: {err}");
73 2
74 }
75 },
76 _ => {
77 eprintln!("usage: tickwise compare <a.rec> <b.rec>");
78 2
79 }
80 },
81 "diff" => match diff::parse_args(rest) {
82 Ok((a, b, mut options)) => {
83 options.color = options.color && color_allowed();
84 match diff::render(&a, &b, &options) {
85 Ok(output) => {
86 print!("{}", output.text);
87 u8::from(output.differs)
88 }
89 Err(err) => {
90 eprintln!("tickwise diff: {err}");
91 2
92 }
93 }
94 }
95 Err(message) => {
96 eprintln!("{message}");
97 2
98 }
99 },
100 "help" | "-h" | "--help" => {
101 print!("{USAGE}");
102 0
103 }
104 "-V" | "--version" => {
105 println!("tickwise {}", env!("CARGO_PKG_VERSION"));
106 0
107 }
108 other => {
109 eprintln!("tickwise: unknown command {other}");
110 eprint!("{USAGE}");
111 2
112 }
113 }
114}