From 486ee9530e45d5e51ed40cc4f69c9e3aaef8c5dc Mon Sep 17 00:00:00 2001 From: Fred Tombs Date: Tue, 28 Apr 2026 15:59:17 -0400 Subject: [PATCH 1/6] Add --parse-only --- fpga_arch_viewer/src/error_report.rs | 157 ++++++++++++++++++ fpga_arch_viewer/src/main.rs | 26 +++ fpga_arch_viewer/src/viewer.rs | 156 +---------------- .../tests/fixtures/invalid_arch.xml | 3 + fpga_arch_viewer/tests/parse_only.rs | 77 +++++++++ 5 files changed, 266 insertions(+), 153 deletions(-) create mode 100644 fpga_arch_viewer/src/error_report.rs create mode 100644 fpga_arch_viewer/tests/fixtures/invalid_arch.xml create mode 100644 fpga_arch_viewer/tests/parse_only.rs diff --git a/fpga_arch_viewer/src/error_report.rs b/fpga_arch_viewer/src/error_report.rs new file mode 100644 index 0000000..6d27989 --- /dev/null +++ b/fpga_arch_viewer/src/error_report.rs @@ -0,0 +1,157 @@ +use fpga_arch_parser::FPGAArchParseError; +use std::io::{BufRead, BufReader}; + +fn get_file_line(file_path: &std::path::Path, line_num: u64) -> Option { + let file = std::fs::File::open(file_path).ok()?; + let reader = BufReader::new(file); + let target = line_num.saturating_sub(1) as usize; + reader.lines().nth(target).and_then(Result::ok) +} + +fn format_context_line(line: &str, column: u64) -> String { + let column = column as usize; + let mut result = format!(" {}\n", line); + let offset = column.saturating_sub(1).min(line.len()); + result.push_str(&format!(" {}{}", " ".repeat(offset), "^")); + result +} + +pub(crate) fn format_parse_error( + error: &FPGAArchParseError, + file_path: Option<&std::path::Path>, +) -> String { + match error { + FPGAArchParseError::ArchFileOpenError(msg) => { + format!("Failed to open architecture file:\n{}", msg) + } + FPGAArchParseError::MissingRequiredTag(tag) => { + format!("Missing required XML tag: {}", tag) + } + FPGAArchParseError::MissingRequiredAttribute(attr, pos) => { + let mut msg = format!( + "Missing required attribute '{}' at line {}, column {}", + attr, + pos.row + 1, + pos.column + 1 + ); + if let Some(path) = file_path + && let Some(line) = get_file_line(path, pos.row + 1) + { + msg.push_str("\n\n"); + msg.push_str(&format_context_line(&line, pos.column + 1)); + } + msg + } + FPGAArchParseError::InvalidTag(tag, pos) => { + let mut msg = format!( + "Invalid or unexpected tag '{}' at line {}, column {}", + tag, + pos.row + 1, + pos.column + 1 + ); + if let Some(path) = file_path + && let Some(line) = get_file_line(path, pos.row + 1) + { + msg.push_str("\n\n"); + msg.push_str(&format_context_line(&line, pos.column + 1)); + } + msg + } + FPGAArchParseError::XMLParseError(msg_text, pos) => { + let mut msg = format!( + "XML parsing error at line {}, column {}:\n{}", + pos.row + 1, + pos.column + 1, + msg_text + ); + if let Some(path) = file_path + && let Some(line) = get_file_line(path, pos.row + 1) + { + msg.push_str("\n\n"); + msg.push_str(&format_context_line(&line, pos.column + 1)); + } + msg + } + FPGAArchParseError::UnknownAttribute(attr, pos) => { + let mut msg = format!( + "Unknown attribute '{}' at line {}, column {}", + attr, + pos.row + 1, + pos.column + 1 + ); + if let Some(path) = file_path + && let Some(line) = get_file_line(path, pos.row + 1) + { + msg.push_str("\n\n"); + msg.push_str(&format_context_line(&line, pos.column + 1)); + } + msg + } + FPGAArchParseError::DuplicateTag(tag, pos) => { + let mut msg = format!( + "Duplicate tag '{}' at line {}, column {}", + tag, + pos.row + 1, + pos.column + 1 + ); + if let Some(path) = file_path + && let Some(line) = get_file_line(path, pos.row + 1) + { + msg.push_str("\n\n"); + msg.push_str(&format_context_line(&line, pos.column + 1)); + } + msg + } + FPGAArchParseError::DuplicateAttribute(attr, pos) => { + let mut msg = format!( + "Duplicate attribute '{}' at line {}, column {}", + attr, + pos.row + 1, + pos.column + 1 + ); + if let Some(path) = file_path + && let Some(line) = get_file_line(path, pos.row + 1) + { + msg.push_str("\n\n"); + msg.push_str(&format_context_line(&line, pos.column + 1)); + } + msg + } + FPGAArchParseError::UnexpectedEndTag(tag, pos) => { + let mut msg = format!( + "Unexpected end tag '' at line {}, column {}", + tag, + pos.row + 1, + pos.column + 1 + ); + if let Some(path) = file_path + && let Some(line) = get_file_line(path, pos.row + 1) + { + msg.push_str("\n\n"); + msg.push_str(&format_context_line(&line, pos.column + 1)); + } + msg + } + FPGAArchParseError::AttributeParseError(msg_text, pos) => { + let mut msg = format!( + "Failed to parse attribute at line {}, column {}:\n{}", + pos.row + 1, + pos.column + 1, + msg_text + ); + if let Some(path) = file_path + && let Some(line) = get_file_line(path, pos.row + 1) + { + msg.push_str("\n\n"); + msg.push_str(&format_context_line(&line, pos.column + 1)); + } + msg + } + FPGAArchParseError::UnexpectedEndOfDocument(msg) => { + format!("Unexpected end of document:\n{}", msg) + } + FPGAArchParseError::PinParsingError(msg) => { + format!("Pin parsing error:\n{}", msg) + } + } +} diff --git a/fpga_arch_viewer/src/main.rs b/fpga_arch_viewer/src/main.rs index fb1bf49..6ed909b 100644 --- a/fpga_arch_viewer/src/main.rs +++ b/fpga_arch_viewer/src/main.rs @@ -8,6 +8,7 @@ mod common_ui; mod complex_block_view; mod crr_sb_view; mod crr_view; +mod error_report; mod grid; mod grid_renderer; mod grid_view; @@ -27,6 +28,31 @@ fn main() -> Result<(), eframe::Error> { // Log to stderr (if you run with `RUST_LOG=debug`). env_logger::init(); + let args: Vec = std::env::args().collect(); + + // --parse-only : parse the architecture file and report errors without opening the GUI. + if let Some(pos) = args.iter().position(|a| a == "--parse-only") { + let Some(file_str) = args.get(pos + 1) else { + eprintln!("error: --parse-only requires a file path"); + std::process::exit(1); + }; + let file_path = std::path::Path::new(file_str); + match fpga_arch_parser::parse(file_path) { + Ok(_) => { + println!("Successfully parsed: {}", file_path.display()); + return Ok(()); + } + Err(e) => { + eprintln!( + "Parse error in {}:\n{}", + file_path.display(), + error_report::format_parse_error(&e, Some(file_path)) + ); + std::process::exit(1); + } + } + } + // Load the icon data. let icon_data = eframe::icon_data::from_png_bytes(&include_bytes!("../assets/icon-256.png")[..]); diff --git a/fpga_arch_viewer/src/viewer.rs b/fpga_arch_viewer/src/viewer.rs index e137eb3..47c357c 100644 --- a/fpga_arch_viewer/src/viewer.rs +++ b/fpga_arch_viewer/src/viewer.rs @@ -1,7 +1,8 @@ use eframe::egui; -use fpga_arch_parser::{FPGAArch, FPGAArchParseError}; +use fpga_arch_parser::FPGAArch; use log::{info, warn}; -use std::io::{BufRead, BufReader}; + +use crate::error_report::format_parse_error; #[cfg(target_arch = "wasm32")] use rfd::AsyncFileDialog; @@ -90,157 +91,6 @@ pub struct FpgaViewer { pending_file_dialog: Option>>, } -fn get_file_line(file_path: &std::path::Path, line_num: u64) -> Option { - let file = std::fs::File::open(file_path).ok()?; - let reader = BufReader::new(file); - let target = line_num.saturating_sub(1) as usize; - reader.lines().nth(target).and_then(Result::ok) -} - -fn format_context_line(line: &str, column: u64) -> String { - let column = column as usize; - let mut result = format!(" {}\n", line); - let offset = column.saturating_sub(1).min(line.len()); - result.push_str(&format!(" {}{}", " ".repeat(offset), "^")); - result -} - -fn format_parse_error(error: &FPGAArchParseError, file_path: Option<&std::path::Path>) -> String { - match error { - FPGAArchParseError::ArchFileOpenError(msg) => { - format!("Failed to open architecture file:\n{}", msg) - } - FPGAArchParseError::MissingRequiredTag(tag) => { - format!("Missing required XML tag: {}", tag) - } - FPGAArchParseError::MissingRequiredAttribute(attr, pos) => { - let mut msg = format!( - "Missing required attribute '{}' at line {}, column {}", - attr, - pos.row + 1, - pos.column + 1 - ); - if let Some(path) = file_path - && let Some(line) = get_file_line(path, pos.row + 1) - { - msg.push_str("\n\n"); - msg.push_str(&format_context_line(&line, pos.column + 1)); - } - msg - } - FPGAArchParseError::InvalidTag(tag, pos) => { - let mut msg = format!( - "Invalid or unexpected tag '{}' at line {}, column {}", - tag, - pos.row + 1, - pos.column + 1 - ); - if let Some(path) = file_path - && let Some(line) = get_file_line(path, pos.row + 1) - { - msg.push_str("\n\n"); - msg.push_str(&format_context_line(&line, pos.column + 1)); - } - msg - } - FPGAArchParseError::XMLParseError(msg_text, pos) => { - let mut msg = format!( - "XML parsing error at line {}, column {}:\n{}", - pos.row + 1, - pos.column + 1, - msg_text - ); - if let Some(path) = file_path - && let Some(line) = get_file_line(path, pos.row + 1) - { - msg.push_str("\n\n"); - msg.push_str(&format_context_line(&line, pos.column + 1)); - } - msg - } - FPGAArchParseError::UnknownAttribute(attr, pos) => { - let mut msg = format!( - "Unknown attribute '{}' at line {}, column {}", - attr, - pos.row + 1, - pos.column + 1 - ); - if let Some(path) = file_path - && let Some(line) = get_file_line(path, pos.row + 1) - { - msg.push_str("\n\n"); - msg.push_str(&format_context_line(&line, pos.column + 1)); - } - msg - } - FPGAArchParseError::DuplicateTag(tag, pos) => { - let mut msg = format!( - "Duplicate tag '{}' at line {}, column {}", - tag, - pos.row + 1, - pos.column + 1 - ); - if let Some(path) = file_path - && let Some(line) = get_file_line(path, pos.row + 1) - { - msg.push_str("\n\n"); - msg.push_str(&format_context_line(&line, pos.column + 1)); - } - msg - } - FPGAArchParseError::DuplicateAttribute(attr, pos) => { - let mut msg = format!( - "Duplicate attribute '{}' at line {}, column {}", - attr, - pos.row + 1, - pos.column + 1 - ); - if let Some(path) = file_path - && let Some(line) = get_file_line(path, pos.row + 1) - { - msg.push_str("\n\n"); - msg.push_str(&format_context_line(&line, pos.column + 1)); - } - msg - } - FPGAArchParseError::UnexpectedEndTag(tag, pos) => { - let mut msg = format!( - "Unexpected end tag '' at line {}, column {}", - tag, - pos.row + 1, - pos.column + 1 - ); - if let Some(path) = file_path - && let Some(line) = get_file_line(path, pos.row + 1) - { - msg.push_str("\n\n"); - msg.push_str(&format_context_line(&line, pos.column + 1)); - } - msg - } - FPGAArchParseError::AttributeParseError(msg_text, pos) => { - let mut msg = format!( - "Failed to parse attribute at line {}, column {}:\n{}", - pos.row + 1, - pos.column + 1, - msg_text - ); - if let Some(path) = file_path - && let Some(line) = get_file_line(path, pos.row + 1) - { - msg.push_str("\n\n"); - msg.push_str(&format_context_line(&line, pos.column + 1)); - } - msg - } - FPGAArchParseError::UnexpectedEndOfDocument(msg) => { - format!("Unexpected end of document:\n{}", msg) - } - FPGAArchParseError::PinParsingError(msg) => { - format!("Pin parsing error:\n{}", msg) - } - } -} impl FpgaViewer { pub fn new() -> Self { diff --git a/fpga_arch_viewer/tests/fixtures/invalid_arch.xml b/fpga_arch_viewer/tests/fixtures/invalid_arch.xml new file mode 100644 index 0000000..37b1930 --- /dev/null +++ b/fpga_arch_viewer/tests/fixtures/invalid_arch.xml @@ -0,0 +1,3 @@ + + + diff --git a/fpga_arch_viewer/tests/parse_only.rs b/fpga_arch_viewer/tests/parse_only.rs new file mode 100644 index 0000000..67d7a5f --- /dev/null +++ b/fpga_arch_viewer/tests/parse_only.rs @@ -0,0 +1,77 @@ +use std::process::Command; + +fn binary() -> &'static str { + env!("CARGO_BIN_EXE_fpga_arch_viewer") +} + +fn valid_arch() -> &'static str { + concat!( + env!("CARGO_MANIFEST_DIR"), + "/../fpga_arch_parser/tests/k4_N4_90nm.xml" + ) +} + +fn invalid_arch() -> &'static str { + concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/fixtures/invalid_arch.xml" + ) +} + +#[test] +fn parse_only_valid_exits_zero() { + let status = Command::new(binary()) + .args(["--parse-only", valid_arch()]) + .status() + .unwrap(); + assert!(status.success()); +} + +#[test] +fn parse_only_valid_prints_success() { + let output = Command::new(binary()) + .args(["--parse-only", valid_arch()]) + .output() + .unwrap(); + assert!(output.status.success()); + let stdout = String::from_utf8_lossy(&output.stdout); + assert!(stdout.contains("Successfully parsed")); +} + +#[test] +fn parse_only_invalid_exits_nonzero() { + let status = Command::new(binary()) + .args(["--parse-only", invalid_arch()]) + .status() + .unwrap(); + assert!(!status.success()); +} + +#[test] +fn parse_only_invalid_reports_error_to_stderr() { + let output = Command::new(binary()) + .args(["--parse-only", invalid_arch()]) + .output() + .unwrap(); + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!(stderr.contains("Parse error")); +} + +#[test] +fn parse_only_missing_file_exits_nonzero() { + let status = Command::new(binary()) + .args(["--parse-only", "/nonexistent/path/arch.xml"]) + .status() + .unwrap(); + assert!(!status.success()); +} + +#[test] +fn parse_only_flag_without_path_exits_nonzero() { + let status = Command::new(binary()) + .arg("--parse-only") + .status() + .unwrap(); + assert!(!status.success()); +} From ebe46a527742af76a378e6538daba02f05805cfe Mon Sep 17 00:00:00 2001 From: Fred Tombs Date: Tue, 28 Apr 2026 16:26:36 -0400 Subject: [PATCH 2/6] Don't open GUI with --parse-only, only accept --parse-only as an arg --- fpga_arch_viewer/src/main.rs | 5 +++++ fpga_arch_viewer/tests/parse_only.rs | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/fpga_arch_viewer/src/main.rs b/fpga_arch_viewer/src/main.rs index 6ed909b..64953cc 100644 --- a/fpga_arch_viewer/src/main.rs +++ b/fpga_arch_viewer/src/main.rs @@ -31,6 +31,11 @@ fn main() -> Result<(), eframe::Error> { let args: Vec = std::env::args().collect(); // --parse-only : parse the architecture file and report errors without opening the GUI. + if let Some(unknown) = args.iter().skip(1).find(|a| a.starts_with('-') && *a != "--parse-only") { + eprintln!("error: unknown argument: {unknown}"); + std::process::exit(1); + } + if let Some(pos) = args.iter().position(|a| a == "--parse-only") { let Some(file_str) = args.get(pos + 1) else { eprintln!("error: --parse-only requires a file path"); diff --git a/fpga_arch_viewer/tests/parse_only.rs b/fpga_arch_viewer/tests/parse_only.rs index 67d7a5f..e965952 100644 --- a/fpga_arch_viewer/tests/parse_only.rs +++ b/fpga_arch_viewer/tests/parse_only.rs @@ -75,3 +75,12 @@ fn parse_only_flag_without_path_exits_nonzero() { .unwrap(); assert!(!status.success()); } + +#[test] +fn unknown_flag_exits_nonzero() { + let status = Command::new(binary()) + .arg("--parse_only") + .status() + .unwrap(); + assert!(!status.success()); +} From 70b04c9e60ec330d537d3f3a375b81b47518360b Mon Sep 17 00:00:00 2001 From: Fred Tombs Date: Wed, 29 Apr 2026 09:32:34 -0400 Subject: [PATCH 3/6] Fix initial file viewer --- fpga_arch_viewer/src/main.rs | 10 ++++++++-- fpga_arch_viewer/src/viewer.rs | 8 ++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/fpga_arch_viewer/src/main.rs b/fpga_arch_viewer/src/main.rs index 64953cc..e8be195 100644 --- a/fpga_arch_viewer/src/main.rs +++ b/fpga_arch_viewer/src/main.rs @@ -58,6 +58,12 @@ fn main() -> Result<(), eframe::Error> { } } + let initial_file = args + .iter() + .skip(1) + .find(|a| !a.starts_with('-')) + .map(std::path::PathBuf::from); + // Load the icon data. let icon_data = eframe::icon_data::from_png_bytes(&include_bytes!("../assets/icon-256.png")[..]); @@ -72,7 +78,7 @@ fn main() -> Result<(), eframe::Error> { eframe::run_native( "FPGA Architecture Visualizer", options, - Box::new(|_cc| Ok(Box::new(viewer::FpgaViewer::new()))), + Box::new(|_cc| Ok(Box::new(viewer::FpgaViewer::new(initial_file)))), ) } @@ -101,7 +107,7 @@ fn main() { .start( canvas, web_options, - Box::new(|_cc| Ok(Box::new(viewer::FpgaViewer::new()))), + Box::new(|_cc| Ok(Box::new(viewer::FpgaViewer::new(None)))), ) .await; diff --git a/fpga_arch_viewer/src/viewer.rs b/fpga_arch_viewer/src/viewer.rs index 47c357c..c574c81 100644 --- a/fpga_arch_viewer/src/viewer.rs +++ b/fpga_arch_viewer/src/viewer.rs @@ -93,8 +93,8 @@ pub struct FpgaViewer { impl FpgaViewer { - pub fn new() -> Self { - Self { + pub fn new(initial_file: Option) -> Self { + let mut viewer = Self { architecture: None, viewer_ctx: ViewerContext { show_about: false, @@ -120,7 +120,11 @@ impl FpgaViewer { fps: 0.0, #[cfg(not(target_arch = "wasm32"))] pending_file_dialog: None, + }; + if let Some(path) = initial_file { + viewer.load_architecture_file(path); } + viewer } fn loaded_arch_filename(&self) -> Option { From 1ff43fea0b96c08c6728119be626b2e52a875e7f Mon Sep 17 00:00:00 2001 From: Fred Tombs Date: Wed, 29 Apr 2026 09:39:23 -0400 Subject: [PATCH 4/6] --parse-only is a flag, not an option --- fpga_arch_viewer/src/main.rs | 20 ++++++++++---------- fpga_arch_viewer/tests/parse_only.rs | 10 +++++----- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/fpga_arch_viewer/src/main.rs b/fpga_arch_viewer/src/main.rs index e8be195..dd82ce0 100644 --- a/fpga_arch_viewer/src/main.rs +++ b/fpga_arch_viewer/src/main.rs @@ -30,18 +30,24 @@ fn main() -> Result<(), eframe::Error> { let args: Vec = std::env::args().collect(); - // --parse-only : parse the architecture file and report errors without opening the GUI. if let Some(unknown) = args.iter().skip(1).find(|a| a.starts_with('-') && *a != "--parse-only") { eprintln!("error: unknown argument: {unknown}"); std::process::exit(1); } - if let Some(pos) = args.iter().position(|a| a == "--parse-only") { - let Some(file_str) = args.get(pos + 1) else { + let parse_only = args.iter().any(|a| a == "--parse-only"); + let initial_file = args + .iter() + .skip(1) + .find(|a| !a.starts_with('-')) + .map(std::path::PathBuf::from); + + // --parse-only: parse the architecture file and report errors without opening the GUI. + if parse_only { + let Some(file_path) = initial_file.as_deref() else { eprintln!("error: --parse-only requires a file path"); std::process::exit(1); }; - let file_path = std::path::Path::new(file_str); match fpga_arch_parser::parse(file_path) { Ok(_) => { println!("Successfully parsed: {}", file_path.display()); @@ -58,12 +64,6 @@ fn main() -> Result<(), eframe::Error> { } } - let initial_file = args - .iter() - .skip(1) - .find(|a| !a.starts_with('-')) - .map(std::path::PathBuf::from); - // Load the icon data. let icon_data = eframe::icon_data::from_png_bytes(&include_bytes!("../assets/icon-256.png")[..]); diff --git a/fpga_arch_viewer/tests/parse_only.rs b/fpga_arch_viewer/tests/parse_only.rs index e965952..dbd23de 100644 --- a/fpga_arch_viewer/tests/parse_only.rs +++ b/fpga_arch_viewer/tests/parse_only.rs @@ -21,7 +21,7 @@ fn invalid_arch() -> &'static str { #[test] fn parse_only_valid_exits_zero() { let status = Command::new(binary()) - .args(["--parse-only", valid_arch()]) + .args([valid_arch(), "--parse-only"]) .status() .unwrap(); assert!(status.success()); @@ -30,7 +30,7 @@ fn parse_only_valid_exits_zero() { #[test] fn parse_only_valid_prints_success() { let output = Command::new(binary()) - .args(["--parse-only", valid_arch()]) + .args([valid_arch(), "--parse-only"]) .output() .unwrap(); assert!(output.status.success()); @@ -41,7 +41,7 @@ fn parse_only_valid_prints_success() { #[test] fn parse_only_invalid_exits_nonzero() { let status = Command::new(binary()) - .args(["--parse-only", invalid_arch()]) + .args([invalid_arch(), "--parse-only"]) .status() .unwrap(); assert!(!status.success()); @@ -50,7 +50,7 @@ fn parse_only_invalid_exits_nonzero() { #[test] fn parse_only_invalid_reports_error_to_stderr() { let output = Command::new(binary()) - .args(["--parse-only", invalid_arch()]) + .args([invalid_arch(), "--parse-only"]) .output() .unwrap(); assert!(!output.status.success()); @@ -61,7 +61,7 @@ fn parse_only_invalid_reports_error_to_stderr() { #[test] fn parse_only_missing_file_exits_nonzero() { let status = Command::new(binary()) - .args(["--parse-only", "/nonexistent/path/arch.xml"]) + .args(["/nonexistent/path/arch.xml", "--parse-only"]) .status() .unwrap(); assert!(!status.success()); From da4404d0d4bb4d7f70ba3f87d856cf5611618237 Mon Sep 17 00:00:00 2001 From: Fred Tombs Date: Wed, 29 Apr 2026 10:55:54 -0400 Subject: [PATCH 5/6] Suppress stderr in error-based tests --- fpga_arch_viewer/tests/parse_only.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fpga_arch_viewer/tests/parse_only.rs b/fpga_arch_viewer/tests/parse_only.rs index dbd23de..bc8707f 100644 --- a/fpga_arch_viewer/tests/parse_only.rs +++ b/fpga_arch_viewer/tests/parse_only.rs @@ -1,4 +1,4 @@ -use std::process::Command; +use std::process::{Command, Stdio}; fn binary() -> &'static str { env!("CARGO_BIN_EXE_fpga_arch_viewer") @@ -22,6 +22,7 @@ fn invalid_arch() -> &'static str { fn parse_only_valid_exits_zero() { let status = Command::new(binary()) .args([valid_arch(), "--parse-only"]) + .stderr(Stdio::null()) .status() .unwrap(); assert!(status.success()); @@ -42,6 +43,7 @@ fn parse_only_valid_prints_success() { fn parse_only_invalid_exits_nonzero() { let status = Command::new(binary()) .args([invalid_arch(), "--parse-only"]) + .stderr(Stdio::null()) .status() .unwrap(); assert!(!status.success()); @@ -62,6 +64,7 @@ fn parse_only_invalid_reports_error_to_stderr() { fn parse_only_missing_file_exits_nonzero() { let status = Command::new(binary()) .args(["/nonexistent/path/arch.xml", "--parse-only"]) + .stderr(Stdio::null()) .status() .unwrap(); assert!(!status.success()); @@ -71,6 +74,7 @@ fn parse_only_missing_file_exits_nonzero() { fn parse_only_flag_without_path_exits_nonzero() { let status = Command::new(binary()) .arg("--parse-only") + .stderr(Stdio::null()) .status() .unwrap(); assert!(!status.success()); @@ -80,6 +84,7 @@ fn parse_only_flag_without_path_exits_nonzero() { fn unknown_flag_exits_nonzero() { let status = Command::new(binary()) .arg("--parse_only") + .stderr(Stdio::null()) .status() .unwrap(); assert!(!status.success()); From b4a52b30adc52e874b6ee5d8a4e809f3efa358e3 Mon Sep 17 00:00:00 2001 From: Fred Tombs Date: Wed, 29 Apr 2026 10:57:46 -0400 Subject: [PATCH 6/6] Formatting --- fpga_arch_viewer/src/main.rs | 6 +++++- fpga_arch_viewer/src/viewer.rs | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/fpga_arch_viewer/src/main.rs b/fpga_arch_viewer/src/main.rs index dd82ce0..29aefa1 100644 --- a/fpga_arch_viewer/src/main.rs +++ b/fpga_arch_viewer/src/main.rs @@ -30,7 +30,11 @@ fn main() -> Result<(), eframe::Error> { let args: Vec = std::env::args().collect(); - if let Some(unknown) = args.iter().skip(1).find(|a| a.starts_with('-') && *a != "--parse-only") { + if let Some(unknown) = args + .iter() + .skip(1) + .find(|a| a.starts_with('-') && *a != "--parse-only") + { eprintln!("error: unknown argument: {unknown}"); std::process::exit(1); } diff --git a/fpga_arch_viewer/src/viewer.rs b/fpga_arch_viewer/src/viewer.rs index c574c81..b639b58 100644 --- a/fpga_arch_viewer/src/viewer.rs +++ b/fpga_arch_viewer/src/viewer.rs @@ -91,7 +91,6 @@ pub struct FpgaViewer { pending_file_dialog: Option>>, } - impl FpgaViewer { pub fn new(initial_file: Option) -> Self { let mut viewer = Self {