use fraction::ToPrimitive;
use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum GpError {
#[error("Unexpected EOF at offset {offset}, needed {needed} bytes")]
UnexpectedEof { offset: usize, needed: usize },
#[error("Invalid value {value} for {context}")]
InvalidValue { context: &'static str, value: i64 },
#[error("String decode error at offset {offset}")]
StringDecode { offset: usize },
#[error("Format error: {0}")]
FormatError(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Type conversion failed for {context}: {value} out of range")]
TypeConversion { context: &'static str, value: i64 },
#[error("Required state '{field}' not set")]
MissingState { field: &'static str },
#[error("Write error: {0}")]
WriteError(String),
}
pub type GpResult<T> = Result<T, GpError>;
impl From<String> for GpError {
fn from(msg: String) -> Self {
GpError::FormatError(msg)
}
}
pub trait ToPrimitiveGp {
fn to_u8_gp(&self, ctx: &'static str) -> GpResult<u8>;
fn to_i8_gp(&self, ctx: &'static str) -> GpResult<i8>;
fn to_i16_gp(&self, ctx: &'static str) -> GpResult<i16>;
fn to_u16_gp(&self, ctx: &'static str) -> GpResult<u16>;
fn to_i32_gp(&self, ctx: &'static str) -> GpResult<i32>;
fn to_i64_gp(&self, ctx: &'static str) -> GpResult<i64>;
fn to_usize_gp(&self, ctx: &'static str) -> GpResult<usize>;
fn to_f32_gp(&self, ctx: &'static str) -> GpResult<f32>;
fn to_f64_gp(&self, ctx: &'static str) -> GpResult<f64>;
}
macro_rules! impl_to_primitive_gp {
($($t:ty),*) => {
$(
impl ToPrimitiveGp for $t {
fn to_u8_gp(&self, ctx: &'static str) -> GpResult<u8> {
self.to_u8().ok_or(GpError::TypeConversion { context: ctx, value: *self as i64 })
}
fn to_i8_gp(&self, ctx: &'static str) -> GpResult<i8> {
self.to_i8().ok_or(GpError::TypeConversion { context: ctx, value: *self as i64 })
}
fn to_i16_gp(&self, ctx: &'static str) -> GpResult<i16> {
self.to_i16().ok_or(GpError::TypeConversion { context: ctx, value: *self as i64 })
}
fn to_u16_gp(&self, ctx: &'static str) -> GpResult<u16> {
self.to_u16().ok_or(GpError::TypeConversion { context: ctx, value: *self as i64 })
}
fn to_i32_gp(&self, ctx: &'static str) -> GpResult<i32> {
self.to_i32().ok_or(GpError::TypeConversion { context: ctx, value: *self as i64 })
}
fn to_i64_gp(&self, ctx: &'static str) -> GpResult<i64> {
self.to_i64().ok_or(GpError::TypeConversion { context: ctx, value: *self as i64 })
}
fn to_usize_gp(&self, ctx: &'static str) -> GpResult<usize> {
self.to_usize().ok_or(GpError::TypeConversion { context: ctx, value: *self as i64 })
}
fn to_f32_gp(&self, ctx: &'static str) -> GpResult<f32> {
self.to_f32().ok_or(GpError::TypeConversion { context: ctx, value: *self as i64 })
}
fn to_f64_gp(&self, ctx: &'static str) -> GpResult<f64> {
self.to_f64().ok_or(GpError::TypeConversion { context: ctx, value: *self as i64 })
}
}
)*
};
}
impl_to_primitive_gp!(i8, u8, i16, u16, i32, u32, i64, u64, isize, usize);
impl ToPrimitiveGp for f32 {
fn to_u8_gp(&self, ctx: &'static str) -> GpResult<u8> {
self.to_u8().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_i8_gp(&self, ctx: &'static str) -> GpResult<i8> {
self.to_i8().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_i16_gp(&self, ctx: &'static str) -> GpResult<i16> {
self.to_i16().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_u16_gp(&self, ctx: &'static str) -> GpResult<u16> {
self.to_u16().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_i32_gp(&self, ctx: &'static str) -> GpResult<i32> {
self.to_i32().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_i64_gp(&self, ctx: &'static str) -> GpResult<i64> {
self.to_i64().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_usize_gp(&self, ctx: &'static str) -> GpResult<usize> {
self.to_usize().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_f32_gp(&self, _ctx: &'static str) -> GpResult<f32> {
Ok(*self)
}
fn to_f64_gp(&self, ctx: &'static str) -> GpResult<f64> {
self.to_f64().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
}
impl ToPrimitiveGp for f64 {
fn to_u8_gp(&self, ctx: &'static str) -> GpResult<u8> {
self.to_u8().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_i8_gp(&self, ctx: &'static str) -> GpResult<i8> {
self.to_i8().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_i16_gp(&self, ctx: &'static str) -> GpResult<i16> {
self.to_i16().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_u16_gp(&self, ctx: &'static str) -> GpResult<u16> {
self.to_u16().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_i32_gp(&self, ctx: &'static str) -> GpResult<i32> {
self.to_i32().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_i64_gp(&self, ctx: &'static str) -> GpResult<i64> {
self.to_i64().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_usize_gp(&self, ctx: &'static str) -> GpResult<usize> {
self.to_usize().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_f32_gp(&self, ctx: &'static str) -> GpResult<f32> {
self.to_f32().ok_or(GpError::TypeConversion {
context: ctx,
value: *self as i64,
})
}
fn to_f64_gp(&self, _ctx: &'static str) -> GpResult<f64> {
Ok(*self)
}
}