Skip to content

Commit f0b2df3

Browse files
authored
Make ListView friendlier to ? syntax (#166)
* PodSliceError ergonomics change * Review updates
1 parent 58994d7 commit f0b2df3

File tree

12 files changed

+163
-97
lines changed

12 files changed

+163
-97
lines changed

Cargo.lock

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

pod/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ bytemuck_derive = { version = "1.10.1" }
1818
num-derive = "0.4"
1919
num_enum = "0.7"
2020
num-traits = "0.2"
21+
pinocchio = "0.9.2"
2122
serde = { version = "1.0.228", optional = true }
2223
solana-program-error = "3.0.0"
2324
solana-program-option = "3.0.0"

pod/src/bytemuck.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! wrappers for `bytemuck` functions
22
3-
use {bytemuck::Pod, solana_program_error::ProgramError};
3+
use {crate::error::SplPodError, bytemuck::Pod};
44

55
/// On-chain size of a `Pod` type
66
pub const fn pod_get_packed_len<T: Pod>() -> usize {
@@ -13,38 +13,36 @@ pub fn pod_bytes_of<T: Pod>(t: &T) -> &[u8] {
1313
}
1414

1515
/// Convert a slice of bytes into a `Pod` (zero copy)
16-
pub fn pod_from_bytes<T: Pod>(bytes: &[u8]) -> Result<&T, ProgramError> {
17-
bytemuck::try_from_bytes(bytes).map_err(|_| ProgramError::InvalidArgument)
16+
pub fn pod_from_bytes<T: Pod>(bytes: &[u8]) -> Result<&T, SplPodError> {
17+
Ok(bytemuck::try_from_bytes(bytes)?)
1818
}
1919

2020
/// Maybe convert a slice of bytes into a `Pod` (zero copy)
2121
///
2222
/// Returns `None` if the slice is empty, or else `Err` if input length is not
2323
/// equal to `pod_get_packed_len::<T>()`.
2424
/// This function exists primarily because `Option<T>` is not a `Pod`.
25-
pub fn pod_maybe_from_bytes<T: Pod>(bytes: &[u8]) -> Result<Option<&T>, ProgramError> {
25+
pub fn pod_maybe_from_bytes<T: Pod>(bytes: &[u8]) -> Result<Option<&T>, SplPodError> {
2626
if bytes.is_empty() {
2727
Ok(None)
2828
} else {
29-
bytemuck::try_from_bytes(bytes)
30-
.map(Some)
31-
.map_err(|_| ProgramError::InvalidArgument)
29+
Ok(bytemuck::try_from_bytes(bytes).map(Some)?)
3230
}
3331
}
3432

3533
/// Convert a slice of bytes into a mutable `Pod` (zero copy)
36-
pub fn pod_from_bytes_mut<T: Pod>(bytes: &mut [u8]) -> Result<&mut T, ProgramError> {
37-
bytemuck::try_from_bytes_mut(bytes).map_err(|_| ProgramError::InvalidArgument)
34+
pub fn pod_from_bytes_mut<T: Pod>(bytes: &mut [u8]) -> Result<&mut T, SplPodError> {
35+
Ok(bytemuck::try_from_bytes_mut(bytes)?)
3836
}
3937

4038
/// Convert a slice of bytes into a `Pod` slice (zero copy)
41-
pub fn pod_slice_from_bytes<T: Pod>(bytes: &[u8]) -> Result<&[T], ProgramError> {
42-
bytemuck::try_cast_slice(bytes).map_err(|_| ProgramError::InvalidArgument)
39+
pub fn pod_slice_from_bytes<T: Pod>(bytes: &[u8]) -> Result<&[T], SplPodError> {
40+
Ok(bytemuck::try_cast_slice(bytes)?)
4341
}
4442

4543
/// Convert a slice of bytes into a mutable `Pod` slice (zero copy)
46-
pub fn pod_slice_from_bytes_mut<T: Pod>(bytes: &mut [u8]) -> Result<&mut [T], ProgramError> {
47-
bytemuck::try_cast_slice_mut(bytes).map_err(|_| ProgramError::InvalidArgument)
44+
pub fn pod_slice_from_bytes_mut<T: Pod>(bytes: &mut [u8]) -> Result<&mut [T], SplPodError> {
45+
Ok(bytemuck::try_cast_slice_mut(bytes)?)
4846
}
4947

5048
/// Convert a `Pod` slice into a single slice of bytes

pod/src/error.rs

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Error types
2+
use pinocchio::program_error::ProgramError as PinocchioProgramError;
23
use {
4+
bytemuck::PodCastError,
35
solana_program_error::{ProgramError, ToStr},
46
std::num::TryFromIntError,
57
};
@@ -15,7 +17,7 @@ use {
1517
num_enum::TryFromPrimitive,
1618
num_derive::FromPrimitive,
1719
)]
18-
pub enum PodSliceError {
20+
pub enum SplPodError {
1921
/// Error in checked math operation
2022
#[error("Error in checked math operation")]
2123
CalculationFailure,
@@ -25,30 +27,90 @@ pub enum PodSliceError {
2527
/// Provided byte buffer too large for expected type
2628
#[error("Provided byte buffer too large for expected type")]
2729
BufferTooLarge,
30+
/// Index out of range for list operation
31+
#[error("Index out of range for list operation")]
32+
IndexOutOfRange,
33+
/// Type used as a length prefix has invalid alignment
34+
#[error("Type used as a length prefix has invalid alignment")]
35+
InvalidLengthTypeAlignment,
36+
/// A `PodCast` operation from `bytemuck` failed
37+
#[error("A `PodCast` operation from `bytemuck` failed")]
38+
PodCast,
2839
/// An integer conversion failed because the value was out of range for the target type
2940
#[error("An integer conversion failed because the value was out of range for the target type")]
3041
ValueOutOfRange,
3142
}
3243

33-
impl From<PodSliceError> for ProgramError {
34-
fn from(e: PodSliceError) -> Self {
44+
impl From<SplPodError> for ProgramError {
45+
fn from(e: SplPodError) -> Self {
3546
ProgramError::Custom(e as u32)
3647
}
3748
}
3849

39-
impl ToStr for PodSliceError {
50+
impl ToStr for SplPodError {
4051
fn to_str(&self) -> &'static str {
4152
match self {
42-
PodSliceError::CalculationFailure => "Error in checked math operation",
43-
PodSliceError::BufferTooSmall => "Provided byte buffer too small for expected type",
44-
PodSliceError::BufferTooLarge => "Provided byte buffer too large for expected type",
45-
PodSliceError::ValueOutOfRange => "An integer conversion failed because the value was out of range for the target type"
53+
SplPodError::CalculationFailure => "Error in checked math operation",
54+
SplPodError::BufferTooSmall => "Provided byte buffer too small for expected type",
55+
SplPodError::BufferTooLarge => "Provided byte buffer too large for expected type",
56+
SplPodError::IndexOutOfRange => "Index out of range for list operation",
57+
SplPodError::InvalidLengthTypeAlignment => "Type used as a length prefix has invalid alignment",
58+
SplPodError::PodCast => "A `PodCast` operation from `bytemuck` failed",
59+
SplPodError::ValueOutOfRange => "An integer conversion failed because the value was out of range for the target type",
4660
}
4761
}
4862
}
4963

50-
impl From<TryFromIntError> for PodSliceError {
64+
impl From<PodCastError> for SplPodError {
65+
fn from(_: PodCastError) -> Self {
66+
SplPodError::PodCast
67+
}
68+
}
69+
70+
impl From<TryFromIntError> for SplPodError {
5171
fn from(_: TryFromIntError) -> Self {
52-
PodSliceError::ValueOutOfRange
72+
SplPodError::ValueOutOfRange
73+
}
74+
}
75+
76+
impl From<SplPodError> for PinocchioProgramError {
77+
fn from(e: SplPodError) -> Self {
78+
PinocchioProgramError::Custom(e as u32)
79+
}
80+
}
81+
82+
#[cfg(test)]
83+
mod test {
84+
use super::*;
85+
use crate::list::ListView;
86+
use pinocchio::program_error::ProgramError as PinocchioProgramError;
87+
88+
fn raises_solana_err() -> Result<(), ProgramError> {
89+
ListView::<u8>::size_of(usize::MAX)?; // raises err
90+
Ok(())
91+
}
92+
93+
fn raises_pino_err() -> Result<(), PinocchioProgramError> {
94+
ListView::<u8>::size_of(usize::MAX)?; // raises err
95+
Ok(())
96+
}
97+
98+
#[test]
99+
fn test_from_pod_slice_error_for_solana_program_error() {
100+
let result = raises_solana_err();
101+
assert!(result.is_err());
102+
let solana_err = result.unwrap_err();
103+
let expected_err: ProgramError = SplPodError::CalculationFailure.into();
104+
assert_eq!(solana_err, expected_err);
105+
}
106+
107+
#[test]
108+
fn test_from_pod_slice_error_for_pinocchio_program_error() {
109+
let result = raises_pino_err();
110+
assert!(result.is_err());
111+
let pinocchio_err = result.unwrap_err();
112+
let expected_solana_err: ProgramError = SplPodError::CalculationFailure.into();
113+
let expected_pinocchio_err: PinocchioProgramError = u64::from(expected_solana_err).into();
114+
assert_eq!(pinocchio_err, expected_pinocchio_err);
53115
}
54116
}

pod/src/list/list_trait.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use {
2-
crate::{list::ListView, pod_length::PodLength},
2+
crate::{error::SplPodError, list::ListView, pod_length::PodLength},
33
bytemuck::Pod,
4-
solana_program_error::ProgramError,
54
std::ops::Deref,
65
};
76

@@ -17,12 +16,12 @@ pub trait List: Deref<Target = [Self::Item]> {
1716
fn capacity(&self) -> usize;
1817

1918
/// Returns the number of **bytes currently occupied** by the live elements
20-
fn bytes_used(&self) -> Result<usize, ProgramError> {
19+
fn bytes_used(&self) -> Result<usize, SplPodError> {
2120
ListView::<Self::Item, Self::Length>::size_of(self.len())
2221
}
2322

2423
/// Returns the number of **bytes reserved** by the entire backing buffer.
25-
fn bytes_allocated(&self) -> Result<usize, ProgramError> {
24+
fn bytes_allocated(&self) -> Result<usize, SplPodError> {
2625
ListView::<Self::Item, Self::Length>::size_of(self.capacity())
2726
}
2827
}

0 commit comments

Comments
 (0)