Skip to main content

binius_examples/
snapshot.rs

1// Copyright 2025 Irreducible Inc.
2// Copyright 2026 The Binius Developers
3use std::{env, fs, path::PathBuf};
4
5use anyhow::{Context, Result};
6use binius_frontend::{Circuit, CircuitStat};
7
8/// Get the workspace root directory by using CARGO_MANIFEST_DIR
9fn workspace_root() -> Result<PathBuf> {
10	let manifest_dir = env::var("CARGO_MANIFEST_DIR").context(
11		"CARGO_MANIFEST_DIR environment variable not set. \
12		Please run this command via 'cargo run -p binius-examples' or 'cargo test'.",
13	)?;
14
15	// CARGO_MANIFEST_DIR points to prover/examples, so go up two levels to reach workspace root
16	let workspace_root = PathBuf::from(manifest_dir)
17		.parent()
18		.and_then(|p| p.parent())
19		.context("Failed to determine workspace root from CARGO_MANIFEST_DIR")?
20		.to_path_buf();
21
22	Ok(workspace_root)
23}
24
25/// Get the snapshot file path for a circuit example
26pub fn snapshot_path(circuit_name: &str) -> Result<PathBuf> {
27	let root = workspace_root()?;
28	Ok(root.join(format!("crates/examples/snapshots/{}.snap", circuit_name)))
29}
30
31/// Format circuit statistics for snapshot
32pub fn format_circuit_stats(circuit_name: &str, circuit: &Circuit) -> String {
33	let mut output = String::new();
34	output.push_str(&format!("{} circuit\n", circuit_name));
35	output.push_str("--\n");
36
37	let stat = CircuitStat::collect(circuit);
38	output.push_str(&format!("{}", stat));
39	output
40}
41
42/// Check if circuit statistics match snapshot
43pub fn check_snapshot(circuit_name: &str, circuit: &Circuit) -> Result<()> {
44	let snapshot_path = snapshot_path(circuit_name)?;
45
46	if !snapshot_path.exists() {
47		anyhow::bail!(
48			"Snapshot file not found at {}\n\
49            Run 'binius-examples {} bless-snapshot' to create it.",
50			snapshot_path.display(),
51			circuit_name
52		);
53	}
54
55	let expected = fs::read_to_string(&snapshot_path)
56		.with_context(|| format!("Failed to read snapshot file: {}", snapshot_path.display()))?;
57
58	let actual = format_circuit_stats(circuit_name, circuit);
59
60	if expected != actual {
61		print_snapshot_diff(&expected, &actual, circuit_name);
62		anyhow::bail!("Circuit statistics do not match snapshot");
63	}
64
65	println!("✓ Circuit statistics match snapshot");
66	Ok(())
67}
68
69/// Update snapshot with current circuit statistics
70pub fn bless_snapshot(circuit_name: &str, circuit: &Circuit) -> Result<()> {
71	let snapshot_path = snapshot_path(circuit_name)?;
72
73	// Create snapshots directory if it doesn't exist
74	if let Some(parent) = snapshot_path.parent() {
75		fs::create_dir_all(parent).with_context(|| {
76			format!("Failed to create snapshot directory: {}", parent.display())
77		})?;
78	}
79
80	let output = format_circuit_stats(circuit_name, circuit);
81
82	fs::write(&snapshot_path, &output)
83		.with_context(|| format!("Failed to write snapshot file: {}", snapshot_path.display()))?;
84
85	println!("✓ Snapshot updated at {}", snapshot_path.display());
86	Ok(())
87}
88
89/// Print a diff between expected and actual snapshots
90fn print_snapshot_diff(expected: &str, actual: &str, circuit_name: &str) {
91	eprintln!("Error: Circuit statistics do not match snapshot!");
92	eprintln!("\n--- Expected (from snapshot) ---");
93	eprintln!("{}", expected);
94	eprintln!("\n--- Actual ---");
95	eprintln!("{}", actual);
96	eprintln!("\n--- Diff ---");
97
98	// Simple line-by-line diff
99	let expected_lines: Vec<_> = expected.lines().collect();
100	let actual_lines: Vec<_> = actual.lines().collect();
101
102	let max_lines = expected_lines.len().max(actual_lines.len());
103	for i in 0..max_lines {
104		let exp_line = expected_lines.get(i).unwrap_or(&"");
105		let act_line = actual_lines.get(i).unwrap_or(&"");
106
107		if exp_line != act_line {
108			eprintln!("Line {}: - {}", i + 1, exp_line);
109			eprintln!("Line {}: + {}", i + 1, act_line);
110		}
111	}
112
113	eprintln!("\nRun 'binius-examples {} bless-snapshot' to update the snapshot.", circuit_name);
114}