use thiserror::Error;
#[derive(Error, Debug)]
pub enum PackError {
#[error("sequence length {length} exceeds capacity {capacity}")]
SequenceTooLong {
length: usize,
capacity: usize,
},
#[error("pack is full, cannot add sequence of length {length}")]
PackFull {
length: usize,
},
#[error("invalid configuration: {message}")]
InvalidConfig {
message: String,
},
#[error("algorithm error: {message}")]
AlgorithmError {
message: String,
},
#[error("empty input: no sequences to pack")]
EmptyInput,
#[error("validation failed: {0}")]
Validation(#[from] ValidationError),
}
#[derive(Error, Debug)]
pub enum ValidationError {
#[error("bin {bin_id}: total {total} exceeds capacity {capacity}")]
CapacityExceeded {
bin_id: usize,
total: usize,
capacity: usize,
},
#[error("bin {bin_id}: accounting mismatch between used and actual sum")]
AccountingMismatch {
bin_id: usize,
},
#[error("item {id} appears in multiple bins")]
DuplicateItem {
id: usize,
},
#[error("not all items appear in the output bins")]
MissingItems,
}
pub type Result<T> = std::result::Result<T, PackError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sequence_too_long_message() {
let err = PackError::SequenceTooLong {
length: 500,
capacity: 128,
};
assert_eq!(err.to_string(), "sequence length 500 exceeds capacity 128");
}
#[test]
fn test_pack_full_message() {
let err = PackError::PackFull { length: 42 };
assert_eq!(
err.to_string(),
"pack is full, cannot add sequence of length 42"
);
}
#[test]
fn test_invalid_config_message() {
let err = PackError::InvalidConfig {
message: "capacity must be > 0".to_string(),
};
assert_eq!(
err.to_string(),
"invalid configuration: capacity must be > 0"
);
}
#[test]
fn test_algorithm_error_message() {
let err = PackError::AlgorithmError {
message: "segment tree overflow".to_string(),
};
assert_eq!(err.to_string(), "algorithm error: segment tree overflow");
}
#[test]
fn test_empty_input_message() {
let err = PackError::EmptyInput;
assert_eq!(err.to_string(), "empty input: no sequences to pack");
}
#[test]
fn test_capacity_exceeded_message() {
let err = ValidationError::CapacityExceeded {
bin_id: 3,
total: 150,
capacity: 128,
};
assert_eq!(err.to_string(), "bin 3: total 150 exceeds capacity 128");
}
#[test]
fn test_accounting_mismatch_message() {
let err = ValidationError::AccountingMismatch { bin_id: 7 };
assert_eq!(
err.to_string(),
"bin 7: accounting mismatch between used and actual sum"
);
}
#[test]
fn test_duplicate_item_message() {
let err = ValidationError::DuplicateItem { id: 42 };
assert_eq!(err.to_string(), "item 42 appears in multiple bins");
}
#[test]
fn test_missing_items_message() {
let err = ValidationError::MissingItems;
assert_eq!(err.to_string(), "not all items appear in the output bins");
}
#[test]
fn test_validation_error_converts_to_pack_error() {
let ve = ValidationError::MissingItems;
let pe: PackError = ve.into();
assert!(matches!(
pe,
PackError::Validation(ValidationError::MissingItems)
));
}
#[test]
fn test_validation_error_wraps_message() {
let ve = ValidationError::CapacityExceeded {
bin_id: 0,
total: 200,
capacity: 100,
};
let pe: PackError = ve.into();
assert_eq!(
pe.to_string(),
"validation failed: bin 0: total 200 exceeds capacity 100"
);
}
}