mirror of
https://github.com/Devolutions/IronRDP.git
synced 2025-12-23 12:26:46 +00:00
fix(ffi): preserve full error context across FFI boundary (#1050)
Some checks failed
CI / Check formatting (push) Has been cancelled
CI / Check typos (push) Has been cancelled
Coverage / Coverage Report (push) Has been cancelled
Release crates / Open release PR (push) Has been cancelled
Release crates / Release crates (push) Has been cancelled
CI / Checks [linux] (push) Has been cancelled
CI / Checks [macos] (push) Has been cancelled
CI / Checks [windows] (push) Has been cancelled
CI / Fuzzing (push) Has been cancelled
CI / Web Client (push) Has been cancelled
CI / FFI (push) Has been cancelled
CI / Success (push) Has been cancelled
Some checks failed
CI / Check formatting (push) Has been cancelled
CI / Check typos (push) Has been cancelled
Coverage / Coverage Report (push) Has been cancelled
Release crates / Open release PR (push) Has been cancelled
Release crates / Release crates (push) Has been cancelled
CI / Checks [linux] (push) Has been cancelled
CI / Checks [macos] (push) Has been cancelled
CI / Checks [windows] (push) Has been cancelled
CI / Fuzzing (push) Has been cancelled
CI / Web Client (push) Has been cancelled
CI / FFI (push) Has been cancelled
CI / Success (push) Has been cancelled
Replaced generic From implementation that used to_string() with specific
implementations for each error type to ensure source error chains are
preserved. IronRDP errors now use .report() for full context, while
standard library errors are converted to anyhow::Error for proper
alternate formatting with {:#}.
This commit is contained in:
parent
632ad86f67
commit
7123150b63
1 changed files with 157 additions and 113 deletions
270
ffi/src/error.rs
270
ffi/src/error.rs
|
|
@ -10,102 +10,6 @@ use ironrdp_rdcleanpath::der;
|
|||
|
||||
use self::ffi::IronRdpErrorKind;
|
||||
|
||||
impl From<ConnectorError> for IronRdpErrorKind {
|
||||
fn from(val: ConnectorError) -> Self {
|
||||
match val.kind() {
|
||||
ironrdp::connector::ConnectorErrorKind::Encode(_) => IronRdpErrorKind::EncodeError,
|
||||
ironrdp::connector::ConnectorErrorKind::Decode(_) => IronRdpErrorKind::DecodeError,
|
||||
ironrdp::connector::ConnectorErrorKind::Credssp(_) => IronRdpErrorKind::CredsspError,
|
||||
ironrdp::connector::ConnectorErrorKind::AccessDenied => IronRdpErrorKind::AccessDenied,
|
||||
_ => IronRdpErrorKind::Generic,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for IronRdpErrorKind {
|
||||
fn from(_val: &str) -> Self {
|
||||
IronRdpErrorKind::Generic
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sspi::Error> for IronRdpErrorKind {
|
||||
fn from(_val: sspi::Error) -> Self {
|
||||
IronRdpErrorKind::CredsspError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ironrdp::pdu::PduError> for IronRdpErrorKind {
|
||||
fn from(_val: ironrdp::pdu::PduError) -> Self {
|
||||
IronRdpErrorKind::PduError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ironrdp::core::EncodeError> for IronRdpErrorKind {
|
||||
fn from(_val: ironrdp::core::EncodeError) -> Self {
|
||||
IronRdpErrorKind::EncodeError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ironrdp::core::DecodeError> for IronRdpErrorKind {
|
||||
fn from(_val: ironrdp::core::DecodeError) -> Self {
|
||||
IronRdpErrorKind::DecodeError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for IronRdpErrorKind {
|
||||
fn from(_: std::io::Error) -> Self {
|
||||
IronRdpErrorKind::IO
|
||||
}
|
||||
}
|
||||
|
||||
impl From<core::fmt::Error> for IronRdpErrorKind {
|
||||
fn from(_val: core::fmt::Error) -> Self {
|
||||
IronRdpErrorKind::Generic
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SessionError> for IronRdpErrorKind {
|
||||
fn from(value: SessionError) -> Self {
|
||||
match value.kind() {
|
||||
ironrdp::session::SessionErrorKind::Pdu(_) => IronRdpErrorKind::PduError,
|
||||
ironrdp::session::SessionErrorKind::Encode(_) => IronRdpErrorKind::EncodeError,
|
||||
ironrdp::session::SessionErrorKind::Decode(_) => IronRdpErrorKind::DecodeError,
|
||||
_ => IronRdpErrorKind::Generic,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&dyn ClipboardError> for IronRdpErrorKind {
|
||||
fn from(_val: &dyn ClipboardError) -> Self {
|
||||
IronRdpErrorKind::Clipboard
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
impl From<WinCliprdrError> for IronRdpErrorKind {
|
||||
fn from(_val: WinCliprdrError) -> Self {
|
||||
IronRdpErrorKind::Clipboard
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WrongOSError> for IronRdpErrorKind {
|
||||
fn from(_val: WrongOSError) -> Self {
|
||||
IronRdpErrorKind::WrongOS
|
||||
}
|
||||
}
|
||||
|
||||
impl From<der::Error> for IronRdpErrorKind {
|
||||
fn from(_val: der::Error) -> Self {
|
||||
IronRdpErrorKind::DecodeError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ironrdp_rdcleanpath::MissingRDCleanPathField> for IronRdpErrorKind {
|
||||
fn from(_val: ironrdp_rdcleanpath::MissingRDCleanPathField) -> Self {
|
||||
IronRdpErrorKind::Generic
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GenericError(pub anyhow::Error);
|
||||
|
||||
impl Display for GenericError {
|
||||
|
|
@ -114,28 +18,168 @@ impl Display for GenericError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<GenericError> for IronRdpErrorKind {
|
||||
fn from(_val: GenericError) -> Self {
|
||||
IronRdpErrorKind::Generic
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Box<ffi::IronRdpError>
|
||||
where
|
||||
T: Into<IronRdpErrorKind> + ToString,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
let repr = value.to_string();
|
||||
let kind = value.into();
|
||||
Box::new(ffi::IronRdpError(IronRdpErrorInner { repr, kind }))
|
||||
}
|
||||
}
|
||||
|
||||
struct IronRdpErrorInner {
|
||||
repr: String,
|
||||
kind: IronRdpErrorKind,
|
||||
}
|
||||
|
||||
// Helper function to create an IronRdpError
|
||||
fn make_ffi_error(repr: String, kind: IronRdpErrorKind) -> Box<ffi::IronRdpError> {
|
||||
Box::new(ffi::IronRdpError(IronRdpErrorInner { repr, kind }))
|
||||
}
|
||||
|
||||
// Direct conversion from IronRdpErrorKind (for cases with no underlying error)
|
||||
impl From<IronRdpErrorKind> for Box<ffi::IronRdpError> {
|
||||
fn from(kind: IronRdpErrorKind) -> Self {
|
||||
make_ffi_error(kind.to_string(), kind)
|
||||
}
|
||||
}
|
||||
|
||||
// IronRDP errors - use .report() to include full error chain with sources
|
||||
impl From<ConnectorError> for Box<ffi::IronRdpError> {
|
||||
fn from(value: ConnectorError) -> Self {
|
||||
let kind = match value.kind() {
|
||||
ironrdp::connector::ConnectorErrorKind::Encode(_) => IronRdpErrorKind::EncodeError,
|
||||
ironrdp::connector::ConnectorErrorKind::Decode(_) => IronRdpErrorKind::DecodeError,
|
||||
ironrdp::connector::ConnectorErrorKind::Credssp(_) => IronRdpErrorKind::CredsspError,
|
||||
ironrdp::connector::ConnectorErrorKind::AccessDenied => IronRdpErrorKind::AccessDenied,
|
||||
_ => IronRdpErrorKind::Generic,
|
||||
};
|
||||
let repr = value.report().to_string();
|
||||
make_ffi_error(repr, kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SessionError> for Box<ffi::IronRdpError> {
|
||||
fn from(value: SessionError) -> Self {
|
||||
let kind = match value.kind() {
|
||||
ironrdp::session::SessionErrorKind::Pdu(_) => IronRdpErrorKind::PduError,
|
||||
ironrdp::session::SessionErrorKind::Encode(_) => IronRdpErrorKind::EncodeError,
|
||||
ironrdp::session::SessionErrorKind::Decode(_) => IronRdpErrorKind::DecodeError,
|
||||
_ => IronRdpErrorKind::Generic,
|
||||
};
|
||||
let repr = value.report().to_string();
|
||||
make_ffi_error(repr, kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ironrdp::pdu::PduError> for Box<ffi::IronRdpError> {
|
||||
fn from(value: ironrdp::pdu::PduError) -> Self {
|
||||
let repr = value.report().to_string();
|
||||
make_ffi_error(repr, IronRdpErrorKind::PduError)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ironrdp::core::EncodeError> for Box<ffi::IronRdpError> {
|
||||
fn from(value: ironrdp::core::EncodeError) -> Self {
|
||||
let repr = value.report().to_string();
|
||||
make_ffi_error(repr, IronRdpErrorKind::EncodeError)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ironrdp::core::DecodeError> for Box<ffi::IronRdpError> {
|
||||
fn from(value: ironrdp::core::DecodeError) -> Self {
|
||||
let repr = value.report().to_string();
|
||||
make_ffi_error(repr, IronRdpErrorKind::DecodeError)
|
||||
}
|
||||
}
|
||||
|
||||
// std::io::Error - convert to anyhow::Error for proper source chain formatting
|
||||
impl From<std::io::Error> for Box<ffi::IronRdpError> {
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
let repr = format!("{:#}", anyhow::Error::new(value));
|
||||
make_ffi_error(repr, IronRdpErrorKind::IO)
|
||||
}
|
||||
}
|
||||
|
||||
// sspi::Error - convert to anyhow::Error for proper source chain formatting
|
||||
impl From<sspi::Error> for Box<ffi::IronRdpError> {
|
||||
fn from(value: sspi::Error) -> Self {
|
||||
let repr = format!("{:#}", anyhow::Error::new(value));
|
||||
make_ffi_error(repr, IronRdpErrorKind::CredsspError)
|
||||
}
|
||||
}
|
||||
|
||||
// Simple string error
|
||||
impl From<&str> for Box<ffi::IronRdpError> {
|
||||
fn from(value: &str) -> Self {
|
||||
make_ffi_error(value.to_owned(), IronRdpErrorKind::Generic)
|
||||
}
|
||||
}
|
||||
|
||||
// core::fmt::Error - convert to anyhow::Error for consistency
|
||||
impl From<core::fmt::Error> for Box<ffi::IronRdpError> {
|
||||
fn from(value: core::fmt::Error) -> Self {
|
||||
let repr = format!("{:#}", anyhow::Error::new(value));
|
||||
make_ffi_error(repr, IronRdpErrorKind::Generic)
|
||||
}
|
||||
}
|
||||
|
||||
// Clipboard errors - manually format with full source chain
|
||||
impl From<&dyn ClipboardError> for Box<ffi::IronRdpError> {
|
||||
fn from(value: &dyn ClipboardError) -> Self {
|
||||
use core::fmt::Write as _;
|
||||
|
||||
// Manually build error chain since we have a trait object reference
|
||||
let mut repr = value.to_string();
|
||||
let mut source = value.source();
|
||||
while let Some(e) = source {
|
||||
let _ = write!(&mut repr, ", caused by: {e}");
|
||||
source = e.source();
|
||||
}
|
||||
make_ffi_error(repr, IronRdpErrorKind::Clipboard)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
impl From<WinCliprdrError> for Box<ffi::IronRdpError> {
|
||||
fn from(value: WinCliprdrError) -> Self {
|
||||
let repr = format!("{:#}", anyhow::Error::new(value));
|
||||
make_ffi_error(repr, IronRdpErrorKind::Clipboard)
|
||||
}
|
||||
}
|
||||
|
||||
// DER errors - convert to anyhow::Error for proper source chain formatting
|
||||
impl From<der::Error> for Box<ffi::IronRdpError> {
|
||||
fn from(value: der::Error) -> Self {
|
||||
let repr = format!("{:#}", anyhow::Error::new(value));
|
||||
make_ffi_error(repr, IronRdpErrorKind::DecodeError)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ironrdp_rdcleanpath::MissingRDCleanPathField> for Box<ffi::IronRdpError> {
|
||||
fn from(value: ironrdp_rdcleanpath::MissingRDCleanPathField) -> Self {
|
||||
let repr = format!("{:#}", anyhow::Error::new(value));
|
||||
make_ffi_error(repr, IronRdpErrorKind::Generic)
|
||||
}
|
||||
}
|
||||
|
||||
// GenericError already has proper Display impl with {:#}
|
||||
impl From<GenericError> for Box<ffi::IronRdpError> {
|
||||
fn from(value: GenericError) -> Self {
|
||||
make_ffi_error(value.to_string(), IronRdpErrorKind::Generic)
|
||||
}
|
||||
}
|
||||
|
||||
// FFI-specific errors
|
||||
impl From<ValueConsumedError> for Box<ffi::IronRdpError> {
|
||||
fn from(value: ValueConsumedError) -> Self {
|
||||
make_ffi_error(value.to_string(), IronRdpErrorKind::Consumed)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IncorrectEnumTypeError> for Box<ffi::IronRdpError> {
|
||||
fn from(value: IncorrectEnumTypeError) -> Self {
|
||||
make_ffi_error(value.to_string(), IronRdpErrorKind::IncorrectEnumType)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WrongOSError> for Box<ffi::IronRdpError> {
|
||||
fn from(value: WrongOSError) -> Self {
|
||||
make_ffi_error(value.to_string(), IronRdpErrorKind::WrongOS)
|
||||
}
|
||||
}
|
||||
|
||||
#[diplomat::bridge]
|
||||
pub mod ffi {
|
||||
use core::fmt::Write as _;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue