//! `wai-quantum repair`: a program repaired from its diagnostics, on the //! lines they flag alone ([`wai_quantum::engine::repair`]). The repaired //! program goes to standard output (or back to its file with `--write`), and //! each repair and what needs a person to standard error; `--json` prints the //! engine's whole answer, its verdict included. //! //! `--proposed FILE` checks a repair someone else wrote (a person's, an //! agent's): it is taken only if it changes no line the diagnostics do not //! flag. Exit 0 when the program reads, 1 when something still needs a //! person, 2 when a proposal is rejected. use serde_json::{Value, json}; use std::path::PathBuf; use wai_quantum::engine::repair; #[derive(clap::Args)] pub struct Args { /// The program; `-` or nothing reads standard input pub file: Option, /// A repaired version to check: taken only if it changes no line but those the diagnostics flag #[arg(long, value_name = "FILE")] pub proposed: Option, /// The bound for an unbounded `while` on measured bits (Q310): it runs at most this many times #[arg(long, value_name = "N")] pub max_iterations: Option, /// Round each angle the bit-exact method refuses (Q500) to the nearest multiple of 2π/2^K, its rounding stated on its line #[arg(long, value_name = "K")] pub dyadic_bits: Option, /// A register's size, for one used before it is declared (Q102), as q=3 (repeatable) #[arg(long = "size", value_name = "NAME=N", value_parser = size_arg)] pub sizes: Vec<(String, u32)>, /// Only check the proposal: make none of the engine's own fixes #[arg(long)] pub no_rules: bool, /// Write the repaired program back to its file #[arg(long, conflicts_with = "json")] pub write: bool, /// Print the engine's answer as JSON: the program, each repair, what needs a person, and the verdict #[arg(long)] pub json: bool, } fn size_arg(s: &str) -> Result<(String, u32), String> { let (k, v) = s.split_once('=').ok_or("give a register and its size, as q=3")?; let n: u32 = v.trim().parse().map_err(|_| format!("{v}: a size is a whole number"))?; Ok((k.trim().to_string(), n)) } /// The repair's choices as the engine takes them. pub fn options(a: &Args, proposed: Option) -> Value { let mut o = json!({}); if let Some(n) = a.max_iterations { o["max_iterations"] = json!(n); } if let Some(k) = a.dyadic_bits { o["dyadic_bits"] = json!(k); } if !a.sizes.is_empty() { o["sizes"] = Value::Object(a.sizes.iter().map(|(k, n)| (k.clone(), json!(n))).collect()); } if let Some(p) = proposed { o["proposed"] = json!(p); } if a.no_rules { o["rules"] = json!(false); } o } /// What was repaired and what needs a person, as a reader takes it in. pub fn report(v: &Value) -> String { let mut s = String::new(); for r in v["repairs"].as_array().into_iter().flatten() { let code = r["code"].as_str().unwrap_or("—"); s += &format!("repaired {code}: {}\n", r["rule"].as_str().unwrap_or("")); for e in r["edits"].as_array().into_iter().flatten() { let line = e["line"].as_u64().unwrap_or(0); match (e["before"].as_str(), e["after"].as_str()) { (Some(b), Some(a)) => s += &format!(" line {line}: {b}\n → {a}\n"), (None, Some(a)) => s += &format!(" added before line {line}: {a}\n"), (Some(b), None) => s += &format!(" removed line {line}: {b}\n"), _ => {} } } } for p in v["needs_person"].as_array().into_iter().flatten() { s += &format!("needs a person {}: {}\n {}\n", p["code"].as_str().unwrap_or(""), p["message"].as_str().unwrap_or(""), p["advice"].as_str().unwrap_or("")); } s += &format!("verdict {}: {}\n", v["verdict"]["level"].as_str().unwrap_or(""), v["verdict"]["statement"].as_str().unwrap_or("")); s } /// `wai-quantum repair`. pub fn main(a: Args) { let src = crate::read_text(a.file.as_deref()).unwrap_or_else(|e| crate::die(&e)); let proposed = a.proposed.as_deref().map(|p| crate::read_text(Some(p)).unwrap_or_else(|e| crate::die(&e))); let v = match repair::repair(&src, &options(&a, proposed)) { Ok(v) => v, Err(e) => { if a.json { println!("{}", json!({ "error": e, "refusal": wai_quantum::engine::refusal::refusal_json(&e) })); } else { eprintln!("wai-quantum: {e}"); } std::process::exit(2); } }; let text = v["qasm"].as_str().unwrap_or(""); if a.json { println!("{v}"); } else if a.write { let Some(path) = a.file.as_deref().filter(|p| *p != std::path::Path::new("-")) else { crate::die("--write needs the program's file") }; if v["changed"] == true { std::fs::write(path, text).unwrap_or_else(|e| crate::die(&format!("{}: {e}", path.display()))); } eprint!("{}", report(&v)); } else { print!("{text}"); eprint!("{}", report(&v)); } std::process::exit(if v["ok"] == true { 0 } else { 1 }); }