ippanel-sms 0.1.0

A professional, asynchronous Rust client library for the IPPANEL SMS web service.
Documentation

use std::fmt;

/// Error codes returned by IPPanel API.
///
/// Each variant represents a specific error code and its meaning.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum ErrorCode {
    Success = 0,
    MessageBodyIsEmpty = 1,
    UserLimited = 2,
    NumberNotAssignedToYou = 3,
    RecipientsEmpty = 4,
    CreditNotEnough = 5,
    NumberNotProfitForBulkSend = 7,
    NumberCannotSendAtThisHour = 9,
    MaxRecipientsExceeded = 98,
    OperatorLineDisconnected = 99,
    AudioFileExtensionInvalid = 21,
    AudioFileSizeInvalid = 22,
    AudioMessageRetryInvalid = 23,
    PhonebookContactInvalid = 100,
    ContactExists = 101,
    ContactSaved = 102,
    MaxRecipientsVoiceNotMet = 111,
    VoiceRetryMustBeOne = 131,
    AudioFileAddressNotEntered = 132,
    UsernameSpecialChar = 301,
    PricingNotSet = 302,
    UsernameNotEntered = 303,
    UsernameAlreadyChosen = 304,
    NationalCodeNotEntered = 306,
    NationalCodeInvalid = 307,
    BirthCertInvalid = 308,
    BirthCertNotEntered = 309,
    EmailNotEntered = 310,
    PhoneNotEntered = 311,
    PhoneIncorrect = 312,
    AddressNotEntered = 313,
    MobileNotEntered = 314,
    MobileIncorrect = 315,
    AccessLevelIncorrect = 316,
    PasswordNotEntered = 317,
    FutureBulkCanceled = 455,
    BulkCodeInvalid = 456,
    TicketCodeInvalid = 458,
    NoAgencyAccess = 964,
    UsernameOrPasswordIncorrect = 962,
    AccessInvalid = 963,
    PatternInvalid = 971,
    PatternParamsInvalid = 970,
    PatternRecipientInvalid = 972,
    SendTimeInvalid = 992,
    PhonebookNotArray = 993,
    NeedBankCardImage = 994,
    NeedBankCardNumber = 995,
    PatternNotActive = 996,
    PatternNotAllowed = 997,
    NationalIdOrCardNotApproved = 998,
    UsernameFormatInvalid = 1001,
    PasswordTooSimple = 1002,
    RegistrationError = 1003,
    RegistrationError2 = 1004,
    RegistrationError3 = 1005,
    Unknown = -1,
}

impl ErrorCode {
    pub fn from_i32(code: i32) -> Self {
        use ErrorCode::*;
        match code {
            0 => Success,
            1 => MessageBodyIsEmpty,
            2 => UserLimited,
            3 => NumberNotAssignedToYou,
            4 => RecipientsEmpty,
            5 => CreditNotEnough,
            7 => NumberNotProfitForBulkSend,
            9 => NumberCannotSendAtThisHour,
            98 => MaxRecipientsExceeded,
            99 => OperatorLineDisconnected,
            21 => AudioFileExtensionInvalid,
            22 => AudioFileSizeInvalid,
            23 => AudioMessageRetryInvalid,
            100 => PhonebookContactInvalid,
            101 => ContactExists,
            102 => ContactSaved,
            111 => MaxRecipientsVoiceNotMet,
            131 => VoiceRetryMustBeOne,
            132 => AudioFileAddressNotEntered,
            301 => UsernameSpecialChar,
            302 => PricingNotSet,
            303 => UsernameNotEntered,
            304 => UsernameAlreadyChosen,
            305 => UsernameNotEntered,
            306 => NationalCodeNotEntered,
            307 => NationalCodeInvalid,
            308 => BirthCertInvalid,
            309 => BirthCertNotEntered,
            310 => EmailNotEntered,
            311 => PhoneNotEntered,
            312 => PhoneIncorrect,
            313 => AddressNotEntered,
            314 => MobileNotEntered,
            315 => MobileIncorrect,
            316 => AccessLevelIncorrect,
            317 => PasswordNotEntered,
            455 => FutureBulkCanceled,
            456 => BulkCodeInvalid,
            458 => TicketCodeInvalid,
            964 => NoAgencyAccess,
            962 => UsernameOrPasswordIncorrect,
            963 => AccessInvalid,
            971 => PatternInvalid,
            970 => PatternParamsInvalid,
            972 => PatternRecipientInvalid,
            992 => SendTimeInvalid,
            993 => PhonebookNotArray,
            994 => NeedBankCardImage,
            995 => NeedBankCardNumber,
            996 => PatternNotActive,
            997 => PatternNotAllowed,
            998 => NationalIdOrCardNotApproved,
            1001 => UsernameFormatInvalid,
            1002 => PasswordTooSimple,
            1003 => RegistrationError,
            1004 => RegistrationError2,
            1005 => RegistrationError3,
            _ => Unknown,
        }
    }

    pub fn code(&self) -> i32 {
        *self as i32
    }

    pub fn english_message(&self) -> &'static str {
        match self {
            ErrorCode::Success => "Operation completed successfully.",
            ErrorCode::MessageBodyIsEmpty => "Message body is empty.",
            ErrorCode::UserLimited => "User is limited.",
            ErrorCode::NumberNotAssignedToYou => "Number not assigned to you.",
            ErrorCode::RecipientsEmpty => "Recipients is empty.",
            ErrorCode::CreditNotEnough => "Credit not enough.",
            ErrorCode::NumberNotProfitForBulkSend => "Number not profit for bulk send.",
            ErrorCode::NumberCannotSendAtThisHour => "Number cannot send at this hour.",
            ErrorCode::MaxRecipientsExceeded => "Maximum recipients number exceeded.",
            ErrorCode::OperatorLineDisconnected => "Operator line is disconnected.",
            ErrorCode::AudioFileExtensionInvalid => "Audio file extension is invalid.",
            ErrorCode::AudioFileSizeInvalid => "Audio file size is invalid.",
            ErrorCode::AudioMessageRetryInvalid => "Audio message retry count is invalid.",
            ErrorCode::PhonebookContactInvalid => "Phonebook contact number is invalid.",
            ErrorCode::ContactExists => "Contact already exists in phonebook.",
            ErrorCode::ContactSaved => "Contact saved successfully in phonebook.",
            ErrorCode::MaxRecipientsVoiceNotMet => "Maximum recipients for voice message not met.",
            ErrorCode::VoiceRetryMustBeOne => "Voice message retry count must be one.",
            ErrorCode::AudioFileAddressNotEntered => "Audio file address not entered.",
            ErrorCode::UsernameSpecialChar => "Special character used in username.",
            ErrorCode::PricingNotSet => "Pricing not set.",
            ErrorCode::UsernameNotEntered => "Username not entered.",
            ErrorCode::UsernameAlreadyChosen => "Username already chosen.",
            ErrorCode::NationalCodeNotEntered => "National code not entered.",
            ErrorCode::NationalCodeInvalid => "National code entered incorrectly.",
            ErrorCode::BirthCertInvalid => "Birth certificate number is invalid.",
            ErrorCode::BirthCertNotEntered => "Birth certificate number not entered.",
            ErrorCode::EmailNotEntered => "User email not entered.",
            ErrorCode::PhoneNotEntered => "Phone number not entered.",
            ErrorCode::PhoneIncorrect => "Phone entered incorrectly.",
            ErrorCode::AddressNotEntered => "Address not entered.",
            ErrorCode::MobileNotEntered => "Mobile number not entered.",
            ErrorCode::MobileIncorrect => "Mobile number entered incorrectly.",
            ErrorCode::AccessLevelIncorrect => "Access level entered incorrectly.",
            ErrorCode::PasswordNotEntered => "Password not entered.",
            ErrorCode::FutureBulkCanceled => "Future sending for bulk code canceled.",
            ErrorCode::BulkCodeInvalid => "Bulk code is invalid.",
            ErrorCode::TicketCodeInvalid => "Ticket code is invalid.",
            ErrorCode::NoAgencyAccess => "You do not have agency access.",
            ErrorCode::UsernameOrPasswordIncorrect => "Username or password is incorrect.",
            ErrorCode::AccessInvalid => "Access is invalid.",
            ErrorCode::PatternInvalid => "Pattern is invalid.",
            ErrorCode::PatternParamsInvalid => "Pattern parameters are invalid.",
            ErrorCode::PatternRecipientInvalid => "Recipient for pattern is invalid.",
            ErrorCode::SendTimeInvalid => "Messages can be sent from 8 to 23.",
            ErrorCode::PhonebookNotArray => "Phonebook must be an array.",
            ErrorCode::NeedBankCardImage => "Please upload a picture of your bank card from the documents menu.",
            ErrorCode::NeedBankCardNumber => "To send with shared lines, please register your bank card number for identity verification.",
            ErrorCode::PatternNotActive => "Pattern is not active.",
            ErrorCode::PatternNotAllowed => "You are not allowed to send from this pattern.",
            ErrorCode::NationalIdOrCardNotApproved => "Your national ID or bank card is not approved.",
            ErrorCode::UsernameFormatInvalid => "Username format is incorrect, at least 5 characters (letters and numbers only).",
            ErrorCode::PasswordTooSimple => "Password is too simple.",
            ErrorCode::RegistrationError | ErrorCode::RegistrationError2 | ErrorCode::RegistrationError3 => "Registration error, contact support.",
            ErrorCode::Unknown => "Unknown error.",
        }
    }

    pub fn persian_message(&self) -> &'static str {
        match self {
            ErrorCode::Success => "عملیات با موفقیت انجام شده است.",
            ErrorCode::MessageBodyIsEmpty => "متن پیام خالی می باشد.",
            ErrorCode::UserLimited => "کاربر محدود گردیده است.",
            ErrorCode::NumberNotAssignedToYou => "خط به شما تعلق ندارد.",
            ErrorCode::RecipientsEmpty => "گیرندگان خالی است.",
            ErrorCode::CreditNotEnough => "اعتبار کافی نیست.",
            ErrorCode::NumberNotProfitForBulkSend => "خط مورد نظر برای ارسال انبوه مناسب نمی‌باشد.",
            ErrorCode::NumberCannotSendAtThisHour => "خط مورد نظر در این ساعت امکان ارسال ندارد.",
            ErrorCode::MaxRecipientsExceeded => "حداکثر تعداد گیرنده رعایت نشده است.",
            ErrorCode::OperatorLineDisconnected => "اپراتور خط ارسالی قطع می‌باشد.",
            ErrorCode::AudioFileExtensionInvalid => "پسوند فایل صوتی نامعتبر است.",
            ErrorCode::AudioFileSizeInvalid => "سایز فایل صوتی نامعتبر است.",
            ErrorCode::AudioMessageRetryInvalid => "تعداد تلاش در پیام صوتی نامعتبر است.",
            ErrorCode::PhonebookContactInvalid => "شماره مخاطب دفترچه تلفن نامعتبر می‌باشد.",
            ErrorCode::ContactExists => "شماره مخاطب در دفترچه تلفن وجود دارد.",
            ErrorCode::ContactSaved => "شماره مخاطب با موفقیت در دفترچه تلفن ذخیره گردید.",
            ErrorCode::MaxRecipientsVoiceNotMet => "حداکثر تعداد گیرنده برای ارسال پیام صوتی رعایت نشده است.",
            ErrorCode::VoiceRetryMustBeOne => "تعداد تلاش در پیام صوتی باید یکبار باشد.",
            ErrorCode::AudioFileAddressNotEntered => "آدرس فایل صوتی وارد نگردیده است.",
            ErrorCode::UsernameSpecialChar => "از حرف ویژه در نام کاربری استفاده گردیده است.",
            ErrorCode::PricingNotSet => "قیمت گذاری انجام نشده است.",
            ErrorCode::UsernameNotEntered => "نام کاربری وارد نگردیده است.",
            ErrorCode::UsernameAlreadyChosen => "نام کاربری قبلا انتخاب گردیده است.",
            ErrorCode::NationalCodeNotEntered => "کد ملی وارد نشده است.",
            ErrorCode::NationalCodeInvalid => "کد ملی به خطا وارد شده است.",
            ErrorCode::BirthCertInvalid => "شماره شناسنامه نا معتبر است.",
            ErrorCode::BirthCertNotEntered => "شماره شناسنامه وارد نگردیده است.",
            ErrorCode::EmailNotEntered => "ایمیل کاربر وارد نگردیده است.",
            ErrorCode::PhoneNotEntered => "شماره تلفن وارد نگردیده است.",
            ErrorCode::PhoneIncorrect => "تلفن به درستی وارد نگردیده است.",
            ErrorCode::AddressNotEntered => "آدرس شما وارد نگردیده است.",
            ErrorCode::MobileNotEntered => "شماره موبایل را وارد نکرده اید.",
            ErrorCode::MobileIncorrect => "شماره موبایل به نادرستی وارد گردیده است.",
            ErrorCode::AccessLevelIncorrect => "سطح دسترسی به نادرستی وارد گردیده است.",
            ErrorCode::PasswordNotEntered => "کلمه عبور وارد نشده است.",
            ErrorCode::FutureBulkCanceled => "ارسال در آینده برای کد بالک ارسالی لغو شد.",
            ErrorCode::BulkCodeInvalid => "کد بالک ارسالی نامعتبر است.",
            ErrorCode::TicketCodeInvalid => "کد تیکت نامعتبر است.",
            ErrorCode::NoAgencyAccess => "شما دسترسی نمایندگی ندارید.",
            ErrorCode::UsernameOrPasswordIncorrect => "نام کاربری یا کلمه عبور نادرست می باشد.",
            ErrorCode::AccessInvalid => "دسترسی نامعتبر می باشد.",
            ErrorCode::PatternInvalid => "پترن ارسالی نامعتبر است.",
            ErrorCode::PatternParamsInvalid => "پارامتر های ارسالی برای پترن نامعتبر است.",
            ErrorCode::PatternRecipientInvalid => "دریافت کننده برای ارسال پترن نامعتبر می باشد.",
            ErrorCode::SendTimeInvalid => "ارسال پیام از ساعت 8 تا 23 می باشد.",
            ErrorCode::PhonebookNotArray => "دفترچه تلفن باید یک آرایه باشد",
            ErrorCode::NeedBankCardImage => "لطفا تصویری از کارت بانکی خود را از منو مدارک ارسال کنید",
            ErrorCode::NeedBankCardNumber => "جهت ارسال با خطوط اشتراکی سامانه، لطفا شماره کارت بانکی خود را به دلیل تکمیل فرایند احراز هویت از بخش ارسال مدارک ثبت نمایید.",
            ErrorCode::PatternNotActive => "پترن فعال نیست.",
            ErrorCode::PatternNotAllowed => "شما اجازه ارسال از این پترن را ندارید.",
            ErrorCode::NationalIdOrCardNotApproved => "کارت ملی یا کارت بانکی شما تایید نشده است.",
            ErrorCode::UsernameFormatInvalid => "فرمت نام کاربری درست نمی باشد،حداقل ۵ کاراکتر(فقط حروف و اعداد)",
            ErrorCode::PasswordTooSimple => "گذرواژه خیلی ساده می باشد.",
            ErrorCode::RegistrationError | ErrorCode::RegistrationError2 | ErrorCode::RegistrationError3 => "مشکل در ثبت، با پشتیبانی تماس بگیرید.",
            ErrorCode::Unknown => "خطای ناشناخته",
        }
    }
}

impl fmt::Display for ErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.english_message())
    }
}

/// Represents an error returned by the IPPanel API or client.
///
/// Contains the error message and the error code as an `ErrorCode` enum.
#[derive(Debug)]
pub struct IppanelError {
    pub message: String,
    pub code: ErrorCode,
}

impl fmt::Display for IppanelError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "IppanelError(code: {}, message: {})", self.code.code(), self.message)
    }
}

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

impl IppanelError {
    pub fn new(message: impl Into<String>, code: i32) -> Self {
        Self { message: message.into(), code: ErrorCode::from_i32(code) }
    }

    pub fn unwrap(&self) -> &str {
        &self.message
    }

    pub fn code(&self) -> ErrorCode {
        self.code
    }

    pub fn parse_api_error(response: &serde_json::Value) -> Option<IppanelError> {
        if let Some(data) = response.get("data") {
            if let Some(error) = data.get("error") {
                let code = response.get("code").and_then(|c| c.as_i64()).unwrap_or(0) as i32;
                return Some(IppanelError::new(error.to_string(), code));
            }
        }
        None
    }

    pub fn to_persian_message(&self) -> &'static str {
        self.code.persian_message()
    }
    pub fn to_english_message(&self) -> &'static str {
        self.code.english_message()
    }
}