Skip to content

Commit

Permalink
Add Python binding for Ipl3ChecksumError
Browse files Browse the repository at this point in the history
  • Loading branch information
AngheloAlf committed Jan 11, 2024
1 parent f423708 commit e879377
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 28 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/ipl3checksum/cickinds.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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).
"""
55 changes: 55 additions & 0 deletions src/ipl3checksum/exceptions.pyi
Original file line number Diff line number Diff line change
@@ -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
"""
2 changes: 2 additions & 0 deletions src/ipl3checksum/ipl3checksum.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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
56 changes: 45 additions & 11 deletions src/rs/error.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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,
Expand All @@ -47,8 +42,47 @@ pub enum Ipl3ChecksumError {
}

#[cfg(feature = "python_bindings")]
impl std::convert::From<Ipl3ChecksumError> 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<super::Ipl3ChecksumError> 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())
}
}
}
}
}
84 changes: 68 additions & 16 deletions src/rs/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<cickinds::CICKind>()?;
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::<super::cickinds::CICKind>()?;

// 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::<super::error::python_bindings::Ipl3ChecksumError>(),
)?;

child_module.add(
"UnalignedRead",
py.get_type::<super::error::python_bindings::UnalignedRead>(),
)?;
child_module.add(
"ByteConversion",
py.get_type::<super::error::python_bindings::ByteConversion>(),
)?;
child_module.add(
"OutOfBounds",
py.get_type::<super::error::python_bindings::OutOfBounds>(),
)?;
child_module.add(
"BufferNotBigEnough",
py.get_type::<super::error::python_bindings::BufferNotBigEnough>(),
)?;
child_module.add(
"BufferSizeIsWrong",
py.get_type::<super::error::python_bindings::BufferSizeIsWrong>(),
)?;
child_module.add(
"UnableToDetectCIC",
py.get_type::<super::error::python_bindings::UnableToDetectCIC>(),
)?;

parent_module.add_submodule(child_module)?;
Ok(())
}
}

#[cfg(test)]
Expand Down

0 comments on commit e879377

Please sign in to comment.