use std::{
    ffi::{CStr, NulError},
    fmt::{self, Display, Formatter},
    io,
};

use num_enum::{IntoPrimitive, TryFromPrimitive};
use singe_core::impl_enum_conversion;
use thiserror::Error;

use singe_cuda_sys::{nvrtc, runtime};

use crate::nvrtc::Status as NvrtcStatus;

#[derive(Error, Debug)]
pub enum Error {
    #[error("cuda error ({code}): {message}")]
    Cuda { code: ErrorCode, message: String },

    #[error("nvrtc error ({code}): {message}")]
    Nvrtc { code: NvrtcStatus, message: String },

    #[error("string contains interior nul byte")]
    InteriorNul,

    #[error("io error: {0}")]
    Io(#[from] io::Error),

    #[error("device not found")]
    DeviceNotFound,

    #[error("invalid memory access")]
    InvalidMemoryAccess,
    #[error("invalid memory allocation request")]
    InvalidMemoryAllocationRequest,
    #[error("unexpected null handle")]
    NullHandle,
    #[error("invalid value")]
    InvalidValue,

    #[error("operation graph required")]
    GraphOperationRequired,
    #[error("graph dependency mismatch")]
    GraphDependencyMismatch,
}

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum ErrorCode {
    Success = runtime::cudaError_t::CUDA_SUCCESS as _,
    InvalidValue = runtime::cudaError_t::CUDA_ERROR_INVALID_VALUE as _,
    OutOfMemory = runtime::cudaError_t::CUDA_ERROR_OUT_OF_MEMORY as _,
    NotInitialized = runtime::cudaError_t::CUDA_ERROR_NOT_INITIALIZED as _,
    Deinitialized = runtime::cudaError_t::CUDA_ERROR_DEINITIALIZED as _,
    ProfilerDisabled = runtime::cudaError_t::CUDA_ERROR_PROFILER_DISABLED as _,
    ProfilerNotInitialized = runtime::cudaError_t::CUDA_ERROR_PROFILER_NOT_INITIALIZED as _,
    ProfilerAlreadyStarted = runtime::cudaError_t::CUDA_ERROR_PROFILER_ALREADY_STARTED as _,
    ProfilerAlreadyStopped = runtime::cudaError_t::CUDA_ERROR_PROFILER_ALREADY_STOPPED as _,
    StubLibrary = runtime::cudaError_t::CUDA_ERROR_STUB_LIBRARY as _,
    CallRequiresNewerDriver = runtime::cudaError_t::CUDA_ERROR_CALL_REQUIRES_NEWER_DRIVER as _,
    DeviceUnavailable = runtime::cudaError_t::CUDA_ERROR_DEVICE_UNAVAILABLE as _,
    NoDevice = runtime::cudaError_t::CUDA_ERROR_NO_DEVICE as _,
    InvalidDevice = runtime::cudaError_t::CUDA_ERROR_INVALID_DEVICE as _,
    DeviceNotLicensed = runtime::cudaError_t::CUDA_ERROR_DEVICE_NOT_LICENSED as _,
    InvalidImage = runtime::cudaError_t::CUDA_ERROR_INVALID_IMAGE as _,
    InvalidContext = runtime::cudaError_t::CUDA_ERROR_INVALID_CONTEXT as _,
    ContextAlreadyCurrent = runtime::cudaError_t::CUDA_ERROR_CONTEXT_ALREADY_CURRENT as _,
    MapFailed = runtime::cudaError_t::CUDA_ERROR_MAP_FAILED as _,
    UnmapFailed = runtime::cudaError_t::CUDA_ERROR_UNMAP_FAILED as _,
    ArrayIsMapped = runtime::cudaError_t::CUDA_ERROR_ARRAY_IS_MAPPED as _,
    AlreadyMapped = runtime::cudaError_t::CUDA_ERROR_ALREADY_MAPPED as _,
    NoBinaryForGpu = runtime::cudaError_t::CUDA_ERROR_NO_BINARY_FOR_GPU as _,
    AlreadyAcquired = runtime::cudaError_t::CUDA_ERROR_ALREADY_ACQUIRED as _,
    NotMapped = runtime::cudaError_t::CUDA_ERROR_NOT_MAPPED as _,
    NotMappedAsArray = runtime::cudaError_t::CUDA_ERROR_NOT_MAPPED_AS_ARRAY as _,
    NotMappedAsPointer = runtime::cudaError_t::CUDA_ERROR_NOT_MAPPED_AS_POINTER as _,
    EccUncorrectable = runtime::cudaError_t::CUDA_ERROR_ECC_UNCORRECTABLE as _,
    UnsupportedLimit = runtime::cudaError_t::CUDA_ERROR_UNSUPPORTED_LIMIT as _,
    ContextAlreadyInUse = runtime::cudaError_t::CUDA_ERROR_CONTEXT_ALREADY_IN_USE as _,
    PeerAccessUnsupported = runtime::cudaError_t::CUDA_ERROR_PEER_ACCESS_UNSUPPORTED as _,
    InvalidPtx = runtime::cudaError_t::CUDA_ERROR_INVALID_PTX as _,
    InvalidGraphicsContext = runtime::cudaError_t::CUDA_ERROR_INVALID_GRAPHICS_CONTEXT as _,
    NvlinkUncorrectable = runtime::cudaError_t::CUDA_ERROR_NVLINK_UNCORRECTABLE as _,
    JitCompilerNotFound = runtime::cudaError_t::CUDA_ERROR_JIT_COMPILER_NOT_FOUND as _,
    UnsupportedPtxVersion = runtime::cudaError_t::CUDA_ERROR_UNSUPPORTED_PTX_VERSION as _,
    JitCompilationDisabled = runtime::cudaError_t::CUDA_ERROR_JIT_COMPILATION_DISABLED as _,
    UnsupportedExecAffinity = runtime::cudaError_t::CUDA_ERROR_UNSUPPORTED_EXEC_AFFINITY as _,
    UnsupportedDevsideSync = runtime::cudaError_t::CUDA_ERROR_UNSUPPORTED_DEVSIDE_SYNC as _,
    Contained = runtime::cudaError_t::CUDA_ERROR_CONTAINED as _,
    InvalidSource = runtime::cudaError_t::CUDA_ERROR_INVALID_SOURCE as _,
    FileNotFound = runtime::cudaError_t::CUDA_ERROR_FILE_NOT_FOUND as _,
    SharedObjectSymbolNotFound =
        runtime::cudaError_t::CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND as _,
    SharedObjectInitFailed = runtime::cudaError_t::CUDA_ERROR_SHARED_OBJECT_INIT_FAILED as _,
    OperatingSystem = runtime::cudaError_t::CUDA_ERROR_OPERATING_SYSTEM as _,
    InvalidHandle = runtime::cudaError_t::CUDA_ERROR_INVALID_HANDLE as _,
    IllegalState = runtime::cudaError_t::CUDA_ERROR_ILLEGAL_STATE as _,
    LossyQuery = runtime::cudaError_t::CUDA_ERROR_LOSSY_QUERY as _,
    NotFound = runtime::cudaError_t::CUDA_ERROR_NOT_FOUND as _,
    NotReady = runtime::cudaError_t::CUDA_ERROR_NOT_READY as _,
    IllegalAddress = runtime::cudaError_t::CUDA_ERROR_ILLEGAL_ADDRESS as _,
    LaunchOutOfResources = runtime::cudaError_t::CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES as _,
    LaunchTimeout = runtime::cudaError_t::CUDA_ERROR_LAUNCH_TIMEOUT as _,
    LaunchIncompatibleTexturing =
        runtime::cudaError_t::CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING as _,
    PeerAccessAlreadyEnabled = runtime::cudaError_t::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED as _,
    PeerAccessNotEnabled = runtime::cudaError_t::CUDA_ERROR_PEER_ACCESS_NOT_ENABLED as _,
    PrimaryContextActive = runtime::cudaError_t::CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE as _,
    ContextIsDestroyed = runtime::cudaError_t::CUDA_ERROR_CONTEXT_IS_DESTROYED as _,
    Assert = runtime::cudaError_t::CUDA_ERROR_ASSERT as _,
    TooManyPeers = runtime::cudaError_t::CUDA_ERROR_TOO_MANY_PEERS as _,
    HostMemoryAlreadyRegistered =
        runtime::cudaError_t::CUDA_ERROR_HOST_MEMORY_ALREADY_REGISTERED as _,
    HostMemoryNotRegistered = runtime::cudaError_t::CUDA_ERROR_HOST_MEMORY_NOT_REGISTERED as _,
    HardwareStackError = runtime::cudaError_t::CUDA_ERROR_HARDWARE_STACK_ERROR as _,
    IllegalInstruction = runtime::cudaError_t::CUDA_ERROR_ILLEGAL_INSTRUCTION as _,
    MisalignedAddress = runtime::cudaError_t::CUDA_ERROR_MISALIGNED_ADDRESS as _,
    InvalidAddressSpace = runtime::cudaError_t::CUDA_ERROR_INVALID_ADDRESS_SPACE as _,
    InvalidPc = runtime::cudaError_t::CUDA_ERROR_INVALID_PC as _,
    LaunchFailed = runtime::cudaError_t::CUDA_ERROR_LAUNCH_FAILED as _,
    CooperativeLaunchTooLarge = runtime::cudaError_t::CUDA_ERROR_COOPERATIVE_LAUNCH_TOO_LARGE as _,
    TensorMemoryLeak = runtime::cudaError_t::CUDA_ERROR_TENSOR_MEMORY_LEAK as _,
    NotPermitted = runtime::cudaError_t::CUDA_ERROR_NOT_PERMITTED as _,
    NotSupported = runtime::cudaError_t::CUDA_ERROR_NOT_SUPPORTED as _,
    SystemNotReady = runtime::cudaError_t::CUDA_ERROR_SYSTEM_NOT_READY as _,
    SystemDriverMismatch = runtime::cudaError_t::CUDA_ERROR_SYSTEM_DRIVER_MISMATCH as _,
    CompatNotSupportedOnDevice =
        runtime::cudaError_t::CUDA_ERROR_COMPAT_NOT_SUPPORTED_ON_DEVICE as _,
    MpsConnectionFailed = runtime::cudaError_t::CUDA_ERROR_MPS_CONNECTION_FAILED as _,
    MpsRpcFailure = runtime::cudaError_t::CUDA_ERROR_MPS_RPC_FAILURE as _,
    MpsServerNotReady = runtime::cudaError_t::CUDA_ERROR_MPS_SERVER_NOT_READY as _,
    MpsMaxClientsReached = runtime::cudaError_t::CUDA_ERROR_MPS_MAX_CLIENTS_REACHED as _,
    MpsMaxConnectionsReached = runtime::cudaError_t::CUDA_ERROR_MPS_MAX_CONNECTIONS_REACHED as _,
    MpsClientTerminated = runtime::cudaError_t::CUDA_ERROR_MPS_CLIENT_TERMINATED as _,
    CdpNotSupported = runtime::cudaError_t::CUDA_ERROR_CDP_NOT_SUPPORTED as _,
    CdpVersionMismatch = runtime::cudaError_t::CUDA_ERROR_CDP_VERSION_MISMATCH as _,
    StreamCaptureUnsupported = runtime::cudaError_t::CUDA_ERROR_STREAM_CAPTURE_UNSUPPORTED as _,
    StreamCaptureInvalidated = runtime::cudaError_t::CUDA_ERROR_STREAM_CAPTURE_INVALIDATED as _,
    StreamCaptureMerge = runtime::cudaError_t::CUDA_ERROR_STREAM_CAPTURE_MERGE as _,
    StreamCaptureUnmatched = runtime::cudaError_t::CUDA_ERROR_STREAM_CAPTURE_UNMATCHED as _,
    StreamCaptureUnjoined = runtime::cudaError_t::CUDA_ERROR_STREAM_CAPTURE_UNJOINED as _,
    StreamCaptureIsolation = runtime::cudaError_t::CUDA_ERROR_STREAM_CAPTURE_ISOLATION as _,
    StreamCaptureImplicit = runtime::cudaError_t::CUDA_ERROR_STREAM_CAPTURE_IMPLICIT as _,
    CapturedEvent = runtime::cudaError_t::CUDA_ERROR_CAPTURED_EVENT as _,
    StreamCaptureWrongThread = runtime::cudaError_t::CUDA_ERROR_STREAM_CAPTURE_WRONG_THREAD as _,
    Timeout = runtime::cudaError_t::CUDA_ERROR_TIMEOUT as _,
    GraphExecUpdateFailure = runtime::cudaError_t::CUDA_ERROR_GRAPH_EXEC_UPDATE_FAILURE as _,
    ExternalDevice = runtime::cudaError_t::CUDA_ERROR_EXTERNAL_DEVICE as _,
    InvalidClusterSize = runtime::cudaError_t::CUDA_ERROR_INVALID_CLUSTER_SIZE as _,
    FunctionNotLoaded = runtime::cudaError_t::CUDA_ERROR_FUNCTION_NOT_LOADED as _,
    InvalidResourceType = runtime::cudaError_t::CUDA_ERROR_INVALID_RESOURCE_TYPE as _,
    InvalidResourceConfiguration =
        runtime::cudaError_t::CUDA_ERROR_INVALID_RESOURCE_CONFIGURATION as _,
    KeyRotation = runtime::cudaError_t::CUDA_ERROR_KEY_ROTATION as _,
    StreamDetached = runtime::cudaError_t::CUDA_ERROR_STREAM_DETACHED as _,
    Unknown = runtime::cudaError_t::CUDA_ERROR_UNKNOWN as _,
}

impl_enum_conversion!(runtime::cudaError_t, ErrorCode);

impl Display for ErrorCode {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::Success => write!(f, "CUDA_SUCCESS"),
            Self::InvalidValue => write!(f, "CUDA_ERROR_INVALID_VALUE"),
            Self::OutOfMemory => write!(f, "CUDA_ERROR_OUT_OF_MEMORY"),
            Self::NotInitialized => write!(f, "CUDA_ERROR_NOT_INITIALIZED"),
            Self::Deinitialized => write!(f, "CUDA_ERROR_DEINITIALIZED"),
            Self::ProfilerDisabled => write!(f, "CUDA_ERROR_PROFILER_DISABLED"),
            Self::ProfilerNotInitialized => write!(f, "CUDA_ERROR_PROFILER_NOT_INITIALIZED"),
            Self::ProfilerAlreadyStarted => write!(f, "CUDA_ERROR_PROFILER_ALREADY_STARTED"),
            Self::ProfilerAlreadyStopped => write!(f, "CUDA_ERROR_PROFILER_ALREADY_STOPPED"),
            Self::StubLibrary => write!(f, "CUDA_ERROR_STUB_LIBRARY"),
            Self::CallRequiresNewerDriver => {
                write!(f, "CUDA_ERROR_CALL_REQUIRES_NEWER_DRIVER")
            }
            Self::DeviceUnavailable => write!(f, "CUDA_ERROR_DEVICE_UNAVAILABLE"),
            Self::NoDevice => write!(f, "CUDA_ERROR_NO_DEVICE"),
            Self::InvalidDevice => write!(f, "CUDA_ERROR_INVALID_DEVICE"),
            Self::DeviceNotLicensed => write!(f, "CUDA_ERROR_DEVICE_NOT_LICENSED"),
            Self::InvalidImage => write!(f, "CUDA_ERROR_INVALID_IMAGE"),
            Self::InvalidContext => write!(f, "CUDA_ERROR_INVALID_CONTEXT"),
            Self::ContextAlreadyCurrent => write!(f, "CUDA_ERROR_CONTEXT_ALREADY_CURRENT"),
            Self::MapFailed => write!(f, "CUDA_ERROR_MAP_FAILED"),
            Self::UnmapFailed => write!(f, "CUDA_ERROR_UNMAP_FAILED"),
            Self::ArrayIsMapped => write!(f, "CUDA_ERROR_ARRAY_IS_MAPPED"),
            Self::AlreadyMapped => write!(f, "CUDA_ERROR_ALREADY_MAPPED"),
            Self::NoBinaryForGpu => write!(f, "CUDA_ERROR_NO_BINARY_FOR_GPU"),
            Self::AlreadyAcquired => write!(f, "CUDA_ERROR_ALREADY_ACQUIRED"),
            Self::NotMapped => write!(f, "CUDA_ERROR_NOT_MAPPED"),
            Self::NotMappedAsArray => write!(f, "CUDA_ERROR_NOT_MAPPED_AS_ARRAY"),
            Self::NotMappedAsPointer => write!(f, "CUDA_ERROR_NOT_MAPPED_AS_POINTER"),
            Self::EccUncorrectable => write!(f, "CUDA_ERROR_ECC_UNCORRECTABLE"),
            Self::UnsupportedLimit => write!(f, "CUDA_ERROR_UNSUPPORTED_LIMIT"),
            Self::ContextAlreadyInUse => write!(f, "CUDA_ERROR_CONTEXT_ALREADY_IN_USE"),
            Self::PeerAccessUnsupported => write!(f, "CUDA_ERROR_PEER_ACCESS_UNSUPPORTED"),
            Self::InvalidPtx => write!(f, "CUDA_ERROR_INVALID_PTX"),
            Self::InvalidGraphicsContext => {
                write!(f, "CUDA_ERROR_INVALID_GRAPHICS_CONTEXT")
            }
            Self::NvlinkUncorrectable => write!(f, "CUDA_ERROR_NVLINK_UNCORRECTABLE"),
            Self::JitCompilerNotFound => write!(f, "CUDA_ERROR_JIT_COMPILER_NOT_FOUND"),
            Self::UnsupportedPtxVersion => write!(f, "CUDA_ERROR_UNSUPPORTED_PTX_VERSION"),
            Self::JitCompilationDisabled => write!(f, "CUDA_ERROR_JIT_COMPILATION_DISABLED"),
            Self::UnsupportedExecAffinity => write!(f, "CUDA_ERROR_UNSUPPORTED_EXEC_AFFINITY"),
            Self::UnsupportedDevsideSync => write!(f, "CUDA_ERROR_UNSUPPORTED_DEVSIDE_SYNC"),
            Self::Contained => write!(f, "CUDA_ERROR_CONTAINED"),
            Self::InvalidSource => write!(f, "CUDA_ERROR_INVALID_SOURCE"),
            Self::FileNotFound => write!(f, "CUDA_ERROR_FILE_NOT_FOUND"),
            Self::SharedObjectSymbolNotFound => {
                write!(f, "CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND")
            }
            Self::SharedObjectInitFailed => write!(f, "CUDA_ERROR_SHARED_OBJECT_INIT_FAILED"),
            Self::OperatingSystem => write!(f, "CUDA_ERROR_OPERATING_SYSTEM"),
            Self::InvalidHandle => write!(f, "CUDA_ERROR_INVALID_HANDLE"),
            Self::IllegalState => write!(f, "CUDA_ERROR_ILLEGAL_STATE"),
            Self::LossyQuery => write!(f, "CUDA_ERROR_LOSSY_QUERY"),
            Self::NotFound => write!(f, "CUDA_ERROR_NOT_FOUND"),
            Self::NotReady => write!(f, "CUDA_ERROR_NOT_READY"),
            Self::IllegalAddress => write!(f, "CUDA_ERROR_ILLEGAL_ADDRESS"),
            Self::LaunchOutOfResources => write!(f, "CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES"),
            Self::LaunchTimeout => write!(f, "CUDA_ERROR_LAUNCH_TIMEOUT"),
            Self::LaunchIncompatibleTexturing => {
                write!(f, "CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING")
            }
            Self::PeerAccessAlreadyEnabled => {
                write!(f, "CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED")
            }
            Self::PeerAccessNotEnabled => write!(f, "CUDA_ERROR_PEER_ACCESS_NOT_ENABLED"),
            Self::PrimaryContextActive => write!(f, "CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE"),
            Self::ContextIsDestroyed => write!(f, "CUDA_ERROR_CONTEXT_IS_DESTROYED"),
            Self::Assert => write!(f, "CUDA_ERROR_ASSERT"),
            Self::TooManyPeers => write!(f, "CUDA_ERROR_TOO_MANY_PEERS"),
            Self::HostMemoryAlreadyRegistered => {
                write!(f, "CUDA_ERROR_HOST_MEMORY_ALREADY_REGISTERED")
            }
            Self::HostMemoryNotRegistered => {
                write!(f, "CUDA_ERROR_HOST_MEMORY_NOT_REGISTERED")
            }
            Self::HardwareStackError => write!(f, "CUDA_ERROR_HARDWARE_STACK_ERROR"),
            Self::IllegalInstruction => write!(f, "CUDA_ERROR_ILLEGAL_INSTRUCTION"),
            Self::MisalignedAddress => write!(f, "CUDA_ERROR_MISALIGNED_ADDRESS"),
            Self::InvalidAddressSpace => write!(f, "CUDA_ERROR_INVALID_ADDRESS_SPACE"),
            Self::InvalidPc => write!(f, "CUDA_ERROR_INVALID_PC"),
            Self::LaunchFailed => write!(f, "CUDA_ERROR_LAUNCH_FAILED"),
            Self::CooperativeLaunchTooLarge => {
                write!(f, "CUDA_ERROR_COOPERATIVE_LAUNCH_TOO_LARGE")
            }
            Self::TensorMemoryLeak => write!(f, "CUDA_ERROR_TENSOR_MEMORY_LEAK"),
            Self::NotPermitted => write!(f, "CUDA_ERROR_NOT_PERMITTED"),
            Self::NotSupported => write!(f, "CUDA_ERROR_NOT_SUPPORTED"),
            Self::SystemNotReady => write!(f, "CUDA_ERROR_SYSTEM_NOT_READY"),
            Self::SystemDriverMismatch => write!(f, "CUDA_ERROR_SYSTEM_DRIVER_MISMATCH"),
            Self::CompatNotSupportedOnDevice => {
                write!(f, "CUDA_ERROR_COMPAT_NOT_SUPPORTED_ON_DEVICE")
            }
            Self::MpsConnectionFailed => write!(f, "CUDA_ERROR_MPS_CONNECTION_FAILED"),
            Self::MpsRpcFailure => write!(f, "CUDA_ERROR_MPS_RPC_FAILURE"),
            Self::MpsServerNotReady => write!(f, "CUDA_ERROR_MPS_SERVER_NOT_READY"),
            Self::MpsMaxClientsReached => write!(f, "CUDA_ERROR_MPS_MAX_CLIENTS_REACHED"),
            Self::MpsMaxConnectionsReached => {
                write!(f, "CUDA_ERROR_MPS_MAX_CONNECTIONS_REACHED")
            }
            Self::MpsClientTerminated => write!(f, "CUDA_ERROR_MPS_CLIENT_TERMINATED"),
            Self::CdpNotSupported => write!(f, "CUDA_ERROR_CDP_NOT_SUPPORTED"),
            Self::CdpVersionMismatch => write!(f, "CUDA_ERROR_CDP_VERSION_MISMATCH"),
            Self::StreamCaptureUnsupported => {
                write!(f, "CUDA_ERROR_STREAM_CAPTURE_UNSUPPORTED")
            }
            Self::StreamCaptureInvalidated => {
                write!(f, "CUDA_ERROR_STREAM_CAPTURE_INVALIDATED")
            }
            Self::StreamCaptureMerge => write!(f, "CUDA_ERROR_STREAM_CAPTURE_MERGE"),
            Self::StreamCaptureUnmatched => write!(f, "CUDA_ERROR_STREAM_CAPTURE_UNMATCHED"),
            Self::StreamCaptureUnjoined => write!(f, "CUDA_ERROR_STREAM_CAPTURE_UNJOINED"),
            Self::StreamCaptureIsolation => write!(f, "CUDA_ERROR_STREAM_CAPTURE_ISOLATION"),
            Self::StreamCaptureImplicit => write!(f, "CUDA_ERROR_STREAM_CAPTURE_IMPLICIT"),
            Self::CapturedEvent => write!(f, "CUDA_ERROR_CAPTURED_EVENT"),
            Self::StreamCaptureWrongThread => {
                write!(f, "CUDA_ERROR_STREAM_CAPTURE_WRONG_THREAD")
            }
            Self::Timeout => write!(f, "CUDA_ERROR_TIMEOUT"),
            Self::GraphExecUpdateFailure => write!(f, "CUDA_ERROR_GRAPH_EXEC_UPDATE_FAILURE"),
            Self::ExternalDevice => write!(f, "CUDA_ERROR_EXTERNAL_DEVICE"),
            Self::InvalidClusterSize => write!(f, "CUDA_ERROR_INVALID_CLUSTER_SIZE"),
            Self::FunctionNotLoaded => write!(f, "CUDA_ERROR_FUNCTION_NOT_LOADED"),
            Self::InvalidResourceType => write!(f, "CUDA_ERROR_INVALID_RESOURCE_TYPE"),
            Self::InvalidResourceConfiguration => {
                write!(f, "CUDA_ERROR_INVALID_RESOURCE_CONFIGURATION")
            }
            Self::KeyRotation => write!(f, "CUDA_ERROR_KEY_ROTATION"),
            Self::StreamDetached => write!(f, "CUDA_ERROR_STREAM_DETACHED"),
            Self::Unknown => write!(f, "CUDA_ERROR_UNKNOWN"),
        }
    }
}

impl From<runtime::cudaError_t> for Error {
    fn from(code: runtime::cudaError_t) -> Self {
        let message = unsafe {
            let c_ptr = runtime::cudaGetErrorString(code);
            if c_ptr.is_null() {
                String::from("unknown cuda error")
            } else {
                CStr::from_ptr(c_ptr).to_string_lossy().into_owned()
            }
        };

        Self::Cuda {
            code: code.into(),
            message,
        }
    }
}

impl From<ErrorCode> for Error {
    fn from(code: ErrorCode) -> Self {
        runtime::cudaError_t::from(code).into()
    }
}

impl From<NulError> for Error {
    fn from(_: NulError) -> Self {
        Self::InteriorNul
    }
}

impl From<nvrtc::nvrtcResult> for Error {
    fn from(code: nvrtc::nvrtcResult) -> Self {
        let message = unsafe {
            let c_ptr = nvrtc::nvrtcGetErrorString(code);
            if c_ptr.is_null() {
                String::from("unknown nvrtc error")
            } else {
                CStr::from_ptr(c_ptr).to_string_lossy().into_owned()
            }
        };

        Self::Nvrtc {
            code: code.into(),
            message,
        }
    }
}

#[macro_export]
macro_rules! try_cuda {
    ($expr:expr) => {{
        let err = { $expr };
        if err != singe_cuda_sys::runtime::cudaError_t::CUDA_SUCCESS {
            Err($crate::error::Error::from(err))
        } else {
            Ok(())
        }
    }};
}

#[macro_export]
macro_rules! try_nvrtc {
    ($expr:expr) => {{
        let err = { $expr };
        if err != singe_cuda_sys::nvrtc::nvrtcResult::NVRTC_SUCCESS {
            Err($crate::error::Error::from(err))
        } else {
            Ok(())
        }
    }};
}
