im-core 0.1.0

Rust IM SDK for Awiki clients built on Agent Network Protocol (ANP)
Documentation
use std::fmt;

pub type ImResult<T> = Result<T, ImError>;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ImError {
    InvalidInput {
        field: Option<String>,
        message: String,
    },
    IdentityRequired,
    IdentityNotFound {
        selector: String,
    },
    DefaultIdentityMissing,
    IdentityNotReady {
        identity: String,
        missing: Vec<String>,
    },
    AuthRequired,
    SessionExpired,
    PermissionDenied,
    PeerNotFound {
        peer: String,
    },
    GroupNotFound {
        group: String,
    },
    MessageNotFound {
        message_id: String,
    },
    TransportUnavailable {
        detail: String,
    },
    UnsupportedCapability {
        capability: String,
    },
    LocalStateUnavailable {
        detail: String,
    },
    PathUnavailable {
        path_kind: String,
        detail: String,
    },
    CredentialFileUnreadable {
        path_kind: String,
        detail: String,
    },
    Service {
        status_code: Option<u16>,
        code: Option<String>,
        message: String,
        data: Option<serde_json::Value>,
    },
    Serialization {
        detail: String,
    },
    Io {
        detail: String,
    },
    Internal {
        message: String,
    },
}

impl ImError {
    pub fn invalid_input(field: impl Into<Option<String>>, message: impl Into<String>) -> Self {
        Self::InvalidInput {
            field: field.into(),
            message: message.into(),
        }
    }

    pub fn unsupported(capability: impl Into<String>) -> Self {
        Self::UnsupportedCapability {
            capability: capability.into(),
        }
    }
}

impl fmt::Display for ImError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::InvalidInput {
                field: Some(field),
                message,
            } => write!(f, "invalid input for {field}: {message}"),
            Self::InvalidInput {
                field: None,
                message,
            } => write!(f, "invalid input: {message}"),
            Self::IdentityRequired => f.write_str("identity is required"),
            Self::IdentityNotFound { selector } => {
                write!(f, "identity not found for selector {selector}")
            }
            Self::DefaultIdentityMissing => f.write_str("default identity is missing"),
            Self::IdentityNotReady { identity, missing } => {
                write!(
                    f,
                    "identity {identity} is not ready: {}",
                    missing.join(", ")
                )
            }
            Self::AuthRequired => f.write_str("authentication is required"),
            Self::SessionExpired => f.write_str("session expired"),
            Self::PermissionDenied => f.write_str("permission denied"),
            Self::PeerNotFound { peer } => write!(f, "peer not found: {peer}"),
            Self::GroupNotFound { group } => write!(f, "group not found: {group}"),
            Self::MessageNotFound { message_id } => write!(f, "message not found: {message_id}"),
            Self::TransportUnavailable { detail } => write!(f, "transport unavailable: {detail}"),
            Self::UnsupportedCapability { capability } => {
                write!(f, "unsupported capability: {capability}")
            }
            Self::LocalStateUnavailable { detail } => {
                write!(f, "local state unavailable: {detail}")
            }
            Self::PathUnavailable { path_kind, detail } => {
                write!(f, "{path_kind} path unavailable: {detail}")
            }
            Self::CredentialFileUnreadable { path_kind, detail } => {
                write!(f, "{path_kind} credential file unreadable: {detail}")
            }
            Self::Service {
                status_code,
                code,
                message,
                data: _,
            } => match (status_code, code) {
                (Some(status), Some(code)) => {
                    write!(f, "service error {status} ({code}): {message}")
                }
                (Some(status), None) => write!(f, "service error {status}: {message}"),
                (None, Some(code)) => write!(f, "service error ({code}): {message}"),
                (None, None) => write!(f, "service error: {message}"),
            },
            Self::Serialization { detail } => write!(f, "serialization error: {detail}"),
            Self::Io { detail } => write!(f, "io error: {detail}"),
            Self::Internal { message } => write!(f, "internal error: {message}"),
        }
    }
}

impl std::error::Error for ImError {}

impl From<std::io::Error> for ImError {
    fn from(value: std::io::Error) -> Self {
        Self::Io {
            detail: value.to_string(),
        }
    }
}

#[cfg(feature = "sqlite")]
impl From<rusqlite::Error> for ImError {
    fn from(value: rusqlite::Error) -> Self {
        Self::LocalStateUnavailable {
            detail: value.to_string(),
        }
    }
}