Mercurial > crates > nonstick
diff src/constants.rs @ 59:3f4a77aa88be
Fix string copyting and improve error situation.
This change is too big and includes several things:
- Fix copying strings from PAM by fixing const and mut on pam funcs.
- Improve error enums by simplifying conversions and removing
unnecessary and ambiguous "success" variants.
- Make a bunch of casts nicer.
- Assorted other cleanup.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Wed, 21 May 2025 00:27:18 -0400 |
parents | daa2cde64601 |
children | 05cc2c27334f |
line wrap: on
line diff
--- a/src/constants.rs Mon May 05 00:16:04 2025 -0400 +++ b/src/constants.rs Wed May 21 00:27:18 2025 -0400 @@ -1,7 +1,9 @@ use bitflags::bitflags; use libc::{c_int, c_uint}; -use num_derive::{FromPrimitive, ToPrimitive}; -use num_traits::{FromPrimitive, ToPrimitive}; +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; +use std::any; +use std::marker::PhantomData; // TODO: Import constants from C header file at compile time. // The Linux-PAM flags @@ -21,7 +23,7 @@ } /// Styles of message that are shown to the user. -#[derive(Debug, PartialEq, FromPrimitive, ToPrimitive)] +#[derive(Debug, PartialEq, FromPrimitive)] #[non_exhaustive] // non-exhaustive because C might give us back anything! pub enum MessageStyle { /// Requests information from the user; will be masked when typing. @@ -39,9 +41,16 @@ BinaryPrompt = 7, } +impl TryFrom<c_int> for MessageStyle { + type Error = InvalidEnum<Self>; + fn try_from(value: c_int) -> Result<Self, Self::Error> { + Self::from_i32(value).ok_or(value.into()) + } +} + impl From<MessageStyle> for c_int { fn from(val: MessageStyle) -> Self { - val.to_i32().unwrap_or(0) + val as Self } } @@ -51,7 +60,7 @@ /// For more detailed information, see /// `/usr/include/security/_pam_types.h`. #[allow(non_camel_case_types, dead_code)] -#[derive(Copy, Clone, Debug, PartialEq, thiserror::Error, FromPrimitive, ToPrimitive)] +#[derive(Copy, Clone, Debug, PartialEq, thiserror::Error, FromPrimitive)] #[non_exhaustive] // C might give us anything! pub enum ErrorCode { #[error("dlopen() failure when dynamically loading a service module")] @@ -129,16 +138,51 @@ } /// Converts a C result code into a PamResult, with success as Ok. + /// Invalid values are returned as a [Self::SystemError]. pub fn result_from(value: c_int) -> PamResult<()> { match value { 0 => Ok(()), - value => Err(Self::from_i64(value as i64).unwrap_or(Self::ConversationError)) + value => Err(value.try_into().unwrap_or(Self::SystemError)), } } } +impl TryFrom<c_int> for ErrorCode { + type Error = InvalidEnum<Self>; + + fn try_from(value: c_int) -> Result<Self, Self::Error> { + Self::from_i32(value).ok_or(value.into()) + } +} + impl From<ErrorCode> for c_int { fn from(val: ErrorCode) -> Self { - val.to_i32().unwrap_or(0) + val as Self + } +} + +/// Error returned when attempting to coerce an invalid C integer into an enum. +#[derive(thiserror::Error)] +#[error("{} is not a valid {}", .0, any::type_name::<T>())] +#[derive(Debug, PartialEq)] +pub struct InvalidEnum<T>(std::ffi::c_int, PhantomData<T>); + +impl<T> From<std::ffi::c_int> for InvalidEnum<T> { + fn from(value: std::ffi::c_int) -> Self { + Self(value, PhantomData) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_enums() { + assert_eq!(Ok(MessageStyle::ErrorMsg), 3.try_into()); + assert_eq!(Err(999.into()), ErrorCode::try_from(999)); + assert_eq!(Ok(()), ErrorCode::result_from(0)); + assert_eq!(Err(ErrorCode::Abort), ErrorCode::result_from(26)); + assert_eq!(Err(ErrorCode::SystemError), ErrorCode::result_from(423)); + } +}