From e879377f4fe29a5b72dfd36ed2429211a8f793b2 Mon Sep 17 00:00:00 2001 From: angie Date: Thu, 11 Jan 2024 17:40:50 -0300 Subject: [PATCH] Add Python binding for Ipl3ChecksumError --- CHANGELOG.md | 3 ++ src/ipl3checksum/cickinds.pyi | 2 +- src/ipl3checksum/exceptions.pyi | 55 ++++++++++++++++++++ src/ipl3checksum/ipl3checksum.pyi | 2 + src/rs/error.rs | 56 +++++++++++++++++---- src/rs/lib.rs | 84 +++++++++++++++++++++++++------ 6 files changed, 174 insertions(+), 28 deletions(-) create mode 100644 src/ipl3checksum/exceptions.pyi diff --git a/CHANGELOG.md b/CHANGELOG.md index f1f7cca..823dc31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 on runtime. - `CICKind.calculate_checksum`: Convinience method that wraps `checksum::calculate_checksum`. +- Python bindings: + - Expose `Ipl3ChecksumError` to Python as a new exception for each error of + the enum. Refer to `ipl3checksum.exceptions`. ### Changed diff --git a/src/ipl3checksum/cickinds.pyi b/src/ipl3checksum/cickinds.pyi index b32509d..a0caf40 100644 --- a/src/ipl3checksum/cickinds.pyi +++ b/src/ipl3checksum/cickinds.pyi @@ -69,5 +69,5 @@ class CICKind(): Returns: tuple[int, int]: If no error happens then the calculated checksum is returned, stored as a tuple - containing two 32-bits words. If an errors occurs an exception will be raised. + containing two 32-bits words. If an errors occurs an exception will be raised (see ipl3checksum.exceptions). """ diff --git a/src/ipl3checksum/exceptions.pyi b/src/ipl3checksum/exceptions.pyi new file mode 100644 index 0000000..4642bf9 --- /dev/null +++ b/src/ipl3checksum/exceptions.pyi @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: © 2024 Decompollaborate +# SPDX-License-Identifier: MIT + +from __future__ import annotations + + +class Ipl3ChecksumError(RuntimeError): + """ + Base exception for all the exceptions raised by this library. + """ + + +class UnalignedRead(Ipl3ChecksumError): + """ + An unaligned read happened. + + (This is probably a library bug, please report me). + """ + +class ByteConversion(Ipl3ChecksumError): + """ + Failed to convert bytes to words. + + (This is probably a library bug, please report me). + """ + +class OutOfBounds(Ipl3ChecksumError): + """ + Tried to access data out of bounds. + + (This is probably a library bug, please report me). + """ + +class BufferNotBigEnough(Ipl3ChecksumError): + """ + The input byte buffer is not big enough. + + The buffer can be larger than the expected size. + + The error runtime string specifies how big the buffer was expected to be. + """ + +class BufferSizeIsWrong(Ipl3ChecksumError): + """ + The input byte buffer didn't have the exact expected size. + + The error runtime string specifies the expected size. + """ + +class UnableToDetectCIC(Ipl3ChecksumError): + """ + Unable to detect CIC variant + """ diff --git a/src/ipl3checksum/ipl3checksum.pyi b/src/ipl3checksum/ipl3checksum.pyi index c205d3c..e23bd56 100644 --- a/src/ipl3checksum/ipl3checksum.pyi +++ b/src/ipl3checksum/ipl3checksum.pyi @@ -12,3 +12,5 @@ from .checksum import calculateChecksumAutodetect as calculateChecksumAutodetect from .detect import detectCIC as detectCIC from .detect import detectCICRaw as detectCICRaw + +import exceptions as exceptions diff --git a/src/rs/error.rs b/src/rs/error.rs index 67863c3..7c35d54 100644 --- a/src/rs/error.rs +++ b/src/rs/error.rs @@ -1,11 +1,6 @@ /* SPDX-FileCopyrightText: © 2023-2024 Decompollaborate */ /* SPDX-License-Identifier: MIT */ -#[cfg(feature = "python_bindings")] -use pyo3::exceptions::PyRuntimeError; -#[cfg(feature = "python_bindings")] -use pyo3::prelude::*; - /* This needs to be in sync with the C equivalent at `bindings/c/include/ipl3checksum/error.h` */ // repr is kinda complex and I may have got it wrong. // I tried to follow the stuff at https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html @@ -22,11 +17,11 @@ pub enum Ipl3ChecksumError { #[error("Failed to convert a FFI string")] StringConversion, - #[error("Unaligned read at offset 0x{offset:X}")] + #[error("Unaligned read at offset 0x{offset:X}. \n (This is probably a library bug, please report me)")] UnalignedRead { offset: usize }, - #[error("Failed to convert bytes at offset 0x{offset:X}")] + #[error("Failed to convert bytes at offset 0x{offset:X} \n (This is probably a library bug, please report me)")] ByteConversion { offset: usize }, - #[error("Tried to access data out of bounds at offset 0x{offset:X}. Requested bytes: 0x{requested_bytes:X}. Buffer length: 0x{buffer_len:X}")] + #[error("Tried to access data out of bounds at offset 0x{offset:X}. Requested bytes: 0x{requested_bytes:X}. Buffer length: 0x{buffer_len:X} \n (This is probably a library bug, please report me)")] OutOfBounds { offset: usize, requested_bytes: usize, @@ -47,8 +42,47 @@ pub enum Ipl3ChecksumError { } #[cfg(feature = "python_bindings")] -impl std::convert::From for PyErr { - fn from(err: Ipl3ChecksumError) -> PyErr { - PyRuntimeError::new_err(err.to_string()) +pub(crate) mod python_bindings { + use pyo3::exceptions::PyRuntimeError; + use pyo3::prelude::*; + + pyo3::create_exception!(ipl3checksum, Ipl3ChecksumError, PyRuntimeError); + + pyo3::create_exception!(ipl3checksum, UnalignedRead, Ipl3ChecksumError); + pyo3::create_exception!(ipl3checksum, ByteConversion, Ipl3ChecksumError); + pyo3::create_exception!(ipl3checksum, OutOfBounds, Ipl3ChecksumError); + pyo3::create_exception!(ipl3checksum, BufferNotBigEnough, Ipl3ChecksumError); + pyo3::create_exception!(ipl3checksum, BufferSizeIsWrong, Ipl3ChecksumError); + pyo3::create_exception!(ipl3checksum, UnableToDetectCIC, Ipl3ChecksumError); + + impl std::convert::From for PyErr { + fn from(err: super::Ipl3ChecksumError) -> PyErr { + match err { + super::Ipl3ChecksumError::UnalignedRead { .. } => { + UnalignedRead::new_err(err.to_string()) + } + super::Ipl3ChecksumError::ByteConversion { .. } => { + ByteConversion::new_err(err.to_string()) + } + super::Ipl3ChecksumError::OutOfBounds { .. } => { + OutOfBounds::new_err(err.to_string()) + } + super::Ipl3ChecksumError::BufferNotBigEnough { .. } => { + BufferNotBigEnough::new_err(err.to_string()) + } + super::Ipl3ChecksumError::BufferSizeIsWrong { .. } => { + BufferSizeIsWrong::new_err(err.to_string()) + } + super::Ipl3ChecksumError::UnableToDetectCIC => { + UnableToDetectCIC::new_err(err.to_string()) + } + #[cfg(feature = "c_bindings")] + super::Ipl3ChecksumError::Okay + | super::Ipl3ChecksumError::NullPointer + | super::Ipl3ChecksumError::StringConversion => { + Ipl3ChecksumError::new_err(err.to_string()) + } + } + } } } diff --git a/src/rs/lib.rs b/src/rs/lib.rs index 9979611..964ec80 100644 --- a/src/rs/lib.rs +++ b/src/rs/lib.rs @@ -14,23 +14,75 @@ pub use detect::*; pub use error::*; #[cfg(feature = "python_bindings")] -use pyo3::prelude::*; +mod python_bindings { + use pyo3::prelude::*; -#[cfg(feature = "python_bindings")] -#[pymodule] -fn ipl3checksum(_py: Python<'_>, m: &PyModule) -> PyResult<()> { - m.add_class::()?; - m.add_function(wrap_pyfunction!( - checksum::python_bindings::calculateChecksum, - m - )?)?; - m.add_function(wrap_pyfunction!( - checksum::python_bindings::calculateChecksumAutodetect, - m - )?)?; - m.add_function(wrap_pyfunction!(detect::python_bindings::detectCICRaw, m)?)?; - m.add_function(wrap_pyfunction!(detect::python_bindings::detectCIC, m)?)?; - Ok(()) + #[pymodule] + fn ipl3checksum(py: Python<'_>, m: &PyModule) -> PyResult<()> { + // Classes + m.add_class::()?; + + // Free functions + m.add_function(wrap_pyfunction!( + super::checksum::python_bindings::calculateChecksum, + m + )?)?; + m.add_function(wrap_pyfunction!( + super::checksum::python_bindings::calculateChecksumAutodetect, + m + )?)?; + m.add_function(wrap_pyfunction!( + super::detect::python_bindings::detectCICRaw, + m + )?)?; + m.add_function(wrap_pyfunction!( + super::detect::python_bindings::detectCIC, + m + )?)?; + + // Exceptions + + register_exceptions_module(py, m)?; + + Ok(()) + } + + fn register_exceptions_module(py: Python<'_>, parent_module: &PyModule) -> PyResult<()> { + let child_module = PyModule::new(py, "exceptions")?; + + child_module.add( + "Ipl3ChecksumError", + py.get_type::(), + )?; + + child_module.add( + "UnalignedRead", + py.get_type::(), + )?; + child_module.add( + "ByteConversion", + py.get_type::(), + )?; + child_module.add( + "OutOfBounds", + py.get_type::(), + )?; + child_module.add( + "BufferNotBigEnough", + py.get_type::(), + )?; + child_module.add( + "BufferSizeIsWrong", + py.get_type::(), + )?; + child_module.add( + "UnableToDetectCIC", + py.get_type::(), + )?; + + parent_module.add_submodule(child_module)?; + Ok(()) + } } #[cfg(test)]