Skip to content

Commit edd6923

Browse files
committed
Simplify error handling
1 parent 52f4031 commit edd6923

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1208
-1131
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ keywords = ["scientific", "quantum", "QEC"]
2121
categories = ["science", "simulation"]
2222

2323
[workspace.dependencies]
24+
thiserror = "2"
2425
rand = "0.9"
2526
rand_chacha = "0.9"
2627
pyo3 = { version = "0.24", features = ["extension-module"] }

crates/pecos-cli/src/engine_setup.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use log::debug;
22
use pecos::prelude::*;
3-
use std::error::Error;
43
use std::path::Path;
54

65
/// Sets up a classical engine for the CLI based on the program type
@@ -9,15 +8,26 @@ use std::path::Path;
98
pub fn setup_cli_engine(
109
program_path: &Path,
1110
shots: Option<usize>,
12-
) -> Result<Box<dyn ClassicalEngine>, Box<dyn Error>> {
11+
) -> Result<Box<dyn ClassicalEngine>, PecosError> {
1312
debug!("Setting up engine for path: {}", program_path.display());
1413

1514
// Create build directory for engine outputs
16-
let build_dir = program_path.parent().unwrap().join("build");
15+
let build_dir = program_path
16+
.parent()
17+
.ok_or_else(|| {
18+
PecosError::Input(format!(
19+
"Cannot determine parent directory for path: {}",
20+
program_path.display()
21+
))
22+
})?
23+
.join("build");
1724
debug!("Build directory: {}", build_dir.display());
18-
std::fs::create_dir_all(&build_dir)?;
25+
std::fs::create_dir_all(&build_dir).map_err(PecosError::IO)?;
1926

20-
match detect_program_type(program_path)? {
27+
// The detect_program_type function now includes proper context in errors
28+
let program_type = detect_program_type(program_path)?;
29+
30+
match program_type {
2131
ProgramType::QIR => {
2232
debug!("Setting up QIR engine");
2333
let mut engine = QirEngine::new(program_path.to_path_buf());

crates/pecos-cli/src/main.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use clap::{Args, Parser, Subcommand};
22
use env_logger::Env;
33
use pecos::prelude::*;
4-
use std::error::Error;
54

65
mod engine_setup;
76
use engine_setup::setup_cli_engine;
@@ -228,8 +227,10 @@ fn parse_general_noise_probabilities(noise_str_opt: Option<&String>) -> (f64, f6
228227
/// This function sets up the appropriate engines and noise models based on
229228
/// the command line arguments, then runs the specified program and outputs
230229
/// the results.
231-
fn run_program(args: &RunArgs) -> Result<(), Box<dyn Error>> {
230+
fn run_program(args: &RunArgs) -> Result<(), PecosError> {
231+
// get_program_path now includes proper context in its errors
232232
let program_path = get_program_path(&args.program)?;
233+
233234
let classical_engine =
234235
setup_cli_engine(&program_path, Some(args.shots.div_ceil(args.workers)))?;
235236

@@ -295,12 +296,15 @@ fn run_program(args: &RunArgs) -> Result<(), Box<dyn Error>> {
295296
// Ensure parent directory exists
296297
if let Some(parent) = std::path::Path::new(file_path).parent() {
297298
if !parent.exists() {
298-
std::fs::create_dir_all(parent)?;
299+
std::fs::create_dir_all(parent).map_err(|e| {
300+
PecosError::Resource(format!("Failed to create directory: {e}"))
301+
})?;
299302
}
300303
}
301304

302305
// Write results to file
303-
std::fs::write(file_path, results_str)?;
306+
std::fs::write(file_path, results_str)
307+
.map_err(|e| PecosError::Resource(format!("Failed to write output file: {e}")))?;
304308
println!("Results written to {file_path}");
305309
}
306310
None => {
@@ -312,18 +316,23 @@ fn run_program(args: &RunArgs) -> Result<(), Box<dyn Error>> {
312316
Ok(())
313317
}
314318

315-
fn main() -> Result<(), Box<dyn Error>> {
319+
fn main() -> Result<(), PecosError> {
316320
// Initialize logger with default "info" level if not specified
317321
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
318322

319323
let cli = Cli::parse();
320324

321325
match &cli.command {
322326
Commands::Compile(args) => {
327+
// get_program_path and detect_program_type now include proper error context
323328
let program_path = get_program_path(&args.program)?;
324-
match detect_program_type(&program_path)? {
329+
330+
let program_type = detect_program_type(&program_path)?;
331+
332+
match program_type {
325333
ProgramType::QIR => {
326334
let engine = setup_cli_engine(&program_path, None)?;
335+
// The compile method should already return a properly formatted PecosError::Compilation
327336
engine.compile()?;
328337
}
329338
ProgramType::PHIR => {

crates/pecos-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ rand.workspace = true
1515
rand_chacha.workspace = true
1616
num-traits.workspace = true
1717
num-complex.workspace = true
18+
thiserror.workspace = true
1819

1920
[lints]
2021
workspace = true

crates/pecos-core/src/errors.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2025 The PECOS Developers
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4+
// in compliance with the License.You may obtain a copy of the License at
5+
//
6+
// https://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software distributed under the License
9+
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10+
// or implied. See the License for the specific language governing permissions and limitations under
11+
// the License.
12+
13+
use std::error::Error;
14+
use std::io;
15+
use thiserror::Error;
16+
17+
/// The main error type for PECOS
18+
#[derive(Error, Debug)]
19+
pub enum PecosError {
20+
/// Input/output related error
21+
#[error("IO error: {0}")]
22+
IO(#[from] io::Error),
23+
24+
/// Generic error when a more specific category doesn't apply
25+
#[error("{0}")]
26+
Generic(String),
27+
28+
/// Error with context information
29+
#[error("{context}: {source}")]
30+
WithContext {
31+
context: String,
32+
#[source]
33+
source: Box<dyn Error + Send + Sync>,
34+
},
35+
36+
/// Error from an external source
37+
#[error(transparent)]
38+
External(#[from] Box<dyn Error + Send + Sync>),
39+
40+
/// Error related to invalid input parameters, arguments, or configuration
41+
#[error("Input error: {0}")]
42+
Input(String),
43+
44+
/// Error related to failures during command or operation processing
45+
#[error("Processing error: {0}")]
46+
Processing(String),
47+
48+
/// Error related to resource handling (files, libraries, etc.)
49+
#[error("Resource error: {0}")]
50+
Resource(String),
51+
52+
/// Error related to the compilation process
53+
#[error("Compilation error: {0}")]
54+
Compilation(String),
55+
56+
/// Error related to an unsupported or invalid quantum gate
57+
#[error("Gate error: {0}")]
58+
Gate(String),
59+
}
60+
61+
impl PecosError {
62+
/// Adds context to any error
63+
pub fn with_context<E, S>(error: E, context: S) -> Self
64+
where
65+
E: Error + Send + Sync + 'static,
66+
S: Into<String>,
67+
{
68+
Self::WithContext {
69+
context: context.into(),
70+
source: Box::new(error),
71+
}
72+
}
73+
}

crates/pecos-core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
pub mod angle;
1414
pub mod element;
15+
pub mod errors;
1516
pub mod gate;
1617
pub mod pauli;
1718
pub mod phase;

crates/pecos-core/src/rng/rng_manageable.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// or implied. See the License for the specific language governing permissions and limitations under
1111
// the License.
1212

13+
use crate::errors::PecosError;
1314
use rand::RngCore;
1415
use rand::SeedableRng;
1516
use rand_chacha::ChaCha8Rng;
@@ -85,7 +86,7 @@ pub trait RngManageable {
8586
///
8687
/// # Errors
8788
/// Returns an error if setting the RNG fails
88-
fn set_rng(&mut self, rng: Self::Rng) -> Result<(), Box<dyn std::error::Error>>;
89+
fn set_rng(&mut self, rng: Self::Rng) -> Result<(), PecosError>;
8990

9091
/// Replace the random number generator with a new one created from a seed
9192
///
@@ -108,7 +109,7 @@ pub trait RngManageable {
108109
/// The default implementation creates a new RNG using `SeedableRng::seed_from_u64`
109110
/// and sets it using `set_rng()`. Implementers typically only need to implement
110111
/// `set_rng()` unless they need custom seed handling.
111-
fn set_seed(&mut self, seed: u64) -> Result<(), Box<dyn std::error::Error>>
112+
fn set_seed(&mut self, seed: u64) -> Result<(), PecosError>
112113
where
113114
Self::Rng: SeedableRng,
114115
{

0 commit comments

Comments
 (0)