1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#![deny(clippy::all)]
#![deny(clippy::pedantic)]
#![deny(missing_docs, warnings, intra_doc_link_resolution_failure)]
#![doc(deny(warnings))]
#![forbid(unsafe_code)]

//! # artichoke-core
//!
//! `artichoke-core` crate provides a set of traits that, when implemented,
//! create a complete Ruby interpreter.
//!
//! [`artichoke-backend`](https://artichoke.github.io/artichoke/artichoke_backend/)
//! is one implementation of the `artichoke-core` traits.
//!
//! ## License
//!
//! artichoke-core is licensed with the [MIT License](/LICENSE) (c) Ryan Lopopolo.

use std::borrow::Cow;
use std::error;
use std::fmt;
use std::io;

pub mod convert;
pub mod eval;
pub mod file;
pub mod load;
pub mod top_self;
pub mod types;
pub mod value;
pub mod warn;

/// Errors returned by Artichoke interpreters.
#[derive(Debug)]
pub enum ArtichokeError {
    /// Failed to convert from a Rust type to a [`Value`](value::Value).
    ConvertToRuby {
        /// Source type of conversion.
        from: types::Rust,
        /// Destination type of conversion.
        to: types::Ruby,
    },
    /// Failed to convert from a [`Value`](value::Value) to a Rust type.
    ConvertToRust {
        /// Source type of conversion.
        from: types::Ruby,
        /// Destination type of conversion.
        to: types::Rust,
    },
    /// Constant name is invalid for the VM backend.
    ///
    /// For example, if the name contains a NUL byte, or is invalid UTF-8.
    InvalidConstantName,
    /// Unable to initalize interpreter.
    New,
    /// Class or module with this name is not defined in the artichoke VM.
    NotDefined(Cow<'static, str>),
    /// Arg count exceeds maximum allowed by the VM.
    TooManyArgs {
        /// Number of arguments supplied.
        given: usize,
        /// Maximum number of arguments supported.
        max: usize,
    },
    /// Attempted to use an uninitialized interpreter.
    Uninitialized,
    /// Attempted to extract Rust object from uninitialized `Value`.
    UninitializedValue(&'static str),
    /// Eval or funcall returned an interpreter-internal value.
    UnreachableValue,
    /// [`io::Error`] when interacting with virtual filesystem.
    ///
    /// See `artichoke_vfs`.
    Vfs(io::Error),
}

impl Eq for ArtichokeError {}

// TODO: remove this impl. I think this is only a kludge for tests.
impl PartialEq for ArtichokeError {
    #[must_use]
    fn eq(&self, other: &Self) -> bool {
        // this is a hack because io::Error does not impl PartialEq
        format!("{}", self) == format!("{}", other)
    }
}

impl fmt::Display for ArtichokeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::ConvertToRuby { from, to } => {
                write!(f, "Failed to convert from {} to {}", from, to)
            }
            Self::ConvertToRust { from, to } => {
                write!(f, "Failed to convert from {} to {}", from, to)
            }
            Self::InvalidConstantName => write!(f, "Invalid constant name"),
            Self::New => write!(f, "Failed to create interpreter"),
            Self::NotDefined(fqname) => write!(f, "{} not defined", fqname),
            Self::TooManyArgs { given, max } => write!(
                f,
                "Too many args for funcall. Gave {}, but max is {}",
                given, max
            ),
            Self::Uninitialized => write!(f, "Interpreter not initialized"),
            Self::UninitializedValue(class) => write!(
                f,
                "Attempted to extract pointer from uninitialized Value with class {}",
                class
            ),
            Self::UnreachableValue => write!(f, "Extracted unreachable type from interpreter"),
            Self::Vfs(err) => write!(f, "mrb vfs io error: {}", err),
        }
    }
}

impl error::Error for ArtichokeError {
    #[must_use]
    fn description(&self) -> &str {
        "Artichoke interpreter error"
    }

    #[must_use]
    fn cause(&self) -> Option<&dyn error::Error> {
        match self {
            Self::Vfs(inner) => Some(inner),
            _ => None,
        }
    }
}